/* * Copyright (c) 2008-2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.placement; 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.eclipse.jetty.util.log.Log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; 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.VirtualArray; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.VpoolProtectionVarraySettings; import com.emc.storageos.db.client.model.VpoolRemoteCopyProtectionSettings; import com.emc.storageos.volumecontroller.Recommendation; import com.emc.storageos.volumecontroller.SRDFRecommendation; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; public class PlacementManager { public static final Logger _log = LoggerFactory.getLogger(PlacementManager.class); /** * An ordered list of schedulers from the top of a hierarchy down. * Currently RecoverPointScheduler is the top and BlockScheduler is the bottom. */ private List<String> schedulerStack; private DbClient dbClient; // Storage schedulers private Map<String, Scheduler> storageSchedulers; // Determine the scheduler to be called. public Scheduler getNextScheduler(String currentScheduler, VirtualPool virtualPool, VpoolUse use) { int index = 0; // If there is a currentScheduler, we only call down the stack if (currentScheduler != null) { while (!schedulerStack.get(index).equals(currentScheduler)) { index++; } // Start with next scheduler after current index++; } // Check the predicate for each scheduler to see if applicable. while (index < schedulerStack.size()) { String schedulerName = schedulerStack.get(index); Scheduler scheduler = storageSchedulers.get(schedulerName); if (scheduler.handlesVpool(virtualPool, use)) { return scheduler; } index++; } // No scheduler type found return null; } public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } public void setStorageSchedulers(Map<String, Scheduler> storageSchedulers) { this.storageSchedulers = storageSchedulers; } public Scheduler getStorageScheduler(String type) { return storageSchedulers.get(type); } /** * Gets a list of recommentdations for a Volume Create Request. * Maintained for compatibility with RP and other existing callers. * @param vArray - Virtual Array object * @param project - Project object * @param vPool - Virtual Pool object * @param capabilities - VirtualPoolCapabilityValuesWrapper contains parameters * @return List of Recommendation */ public List getRecommendationsForVolumeCreateRequest(VirtualArray vArray, Project project, VirtualPool vPool, VirtualPoolCapabilityValuesWrapper capabilities) { // Get the volume placement based on passed parameters. Map<VpoolUse, List<Recommendation>> currentRecommendations = new HashMap<VpoolUse, List<Recommendation>>(); Scheduler scheduler = getBlockServiceImpl(vPool); List<Recommendation> recommendations = scheduler.getRecommendationsForVpool( vArray, project, vPool, VpoolUse.ROOT, capabilities, currentRecommendations); return recommendations; } /** * A call that can return multiple placement results, one for the ROOT level Vpool, but also others * for example for SRDF_COPY. The output is a map of Vpool use to the list of recommendations for that Vpool. * @param virtualArray - Virtual Array object * @param project - Project object * @param virtualPool- Virtual Pool object * @param capabilities - VirtualPoolCapabilityValuesWrapper contains parameters * @return Map of VpoolUse to List of Recommendations for that use. */ public Map<VpoolUse, List<Recommendation>> getRecommendationsForVirtualPool(VirtualArray virtualArray, Project project, VirtualPool virtualPool, VirtualPoolCapabilityValuesWrapper capabilities) { Map<VpoolUse, List<Recommendation>> recommendationMap = new HashMap<VpoolUse, List<Recommendation>>(); // Invoke scheduling on the top level Virtual Pool (termed ROOT). This virtual pool // may have within it other virtual pools that may need to be separately scheduled. VpoolUse use = VpoolUse.ROOT; // the apisvc vpool Scheduler scheduler = getNextScheduler(null, virtualPool, use); List<Recommendation> newRecommendations = scheduler.getRecommendationsForVpool( virtualArray, project, virtualPool, use, capabilities, recommendationMap); if (newRecommendations.isEmpty()) { return recommendationMap; } recommendationMap.put(use, newRecommendations); // VPLEX will automatically take care of the VPLEX_HA virtual pool, // so it is not invoked specifically here. // Loop over the SRDF Copies, invoking a scheduler on them. if (VirtualPool.vPoolSpecifiesSRDF(virtualPool)) { Map<URI, VpoolRemoteCopyProtectionSettings> remoteCopyMap = VirtualPool.getRemoteProtectionSettings(virtualPool, dbClient); for (Map.Entry<URI, VpoolRemoteCopyProtectionSettings> entry : remoteCopyMap.entrySet()) { // Invoke scheduler on SRDF copies use = VpoolUse.SRDF_COPY; URI vArrayURI = entry.getValue().getVirtualArray(); VirtualArray vArray = dbClient.queryObject(VirtualArray.class, vArrayURI); URI vPoolURI = entry.getValue().getVirtualPool(); VirtualPool vPool = dbClient.queryObject(VirtualPool.class, vPoolURI); scheduler = getNextScheduler(null, vPool, use); newRecommendations = scheduler.getRecommendationsForVpool( vArray, project, vPool, use, capabilities, recommendationMap); if (recommendationMap.containsKey(use)) { recommendationMap.get(use).addAll(newRecommendations); } else { recommendationMap.put(use, newRecommendations); } } } logRecommendations(recommendationMap); return recommendationMap; } /** * Returns the scheduler responsible for scheduling resources * * @param vpool Virtual Pool * @return storage scheduler */ private Scheduler getBlockServiceImpl(VirtualPool vpool) { // Select an implementation of the right scheduler Scheduler scheduler; if (VirtualPool.vPoolSpecifiesProtection(vpool)) { scheduler = storageSchedulers.get("rp"); } else if (VirtualPool.vPoolSpecifiesHighAvailability(vpool)) { scheduler = storageSchedulers.get("vplex"); } else if (VirtualPool.vPoolSpecifiesSRDF(vpool)) { scheduler = storageSchedulers.get("srdf"); } else { scheduler = storageSchedulers.get("block"); } return scheduler; } /** * Determines if any of the RP targets has HA (VPlex local/distributed) * specified. This method should be called in determining if the * rpvplex scheduler should be used, specifically for non-virtual to * virtual protection. This is currently not supported and is why the * condition was removed from getBlockServiceImpl(). * * @return true if there is at least 1 HA RP copy target. */ private boolean hasHaRpCopyTarget(VirtualPool vpool) { // Check if any of the RP copies contains a protection virtual pool that specifies // high availability. This would indicate mixed protection, therefore we need // to use the RP/VPLEX scheduler. boolean rpVPlex = false; if (vpool.getProtectionVarraySettings() != null && !vpool.getProtectionVarraySettings().isEmpty()) { for (String protectionVarraURI : vpool.getProtectionVarraySettings().keySet()) { String settingsURI = vpool.getProtectionVarraySettings().get(protectionVarraURI); VpoolProtectionVarraySettings settings = dbClient.queryObject( VpoolProtectionVarraySettings.class, URI.create(settingsURI)); URI protectionVpoolId = vpool.getId(); if (settings.getVirtualPool() != null) { protectionVpoolId = settings.getVirtualPool(); } VirtualPool protectionVpool = dbClient.queryObject(VirtualPool.class, protectionVpoolId); if (VirtualPool.vPoolSpecifiesHighAvailability(protectionVpool)) { rpVPlex = true; break; } } } return rpVPlex; } /** * Return the StoragePools from a list of Recommendations. * @param recommendations List of Recommendation objects. * @return List<StoragePool */ public List<StoragePool> getStoragePoolsFromRecommendations(List<Recommendation> recommendations) { List<StoragePool> storagePools = new ArrayList<StoragePool>(); for (Recommendation recommendation : recommendations) { StoragePool storagePool = dbClient.queryObject( StoragePool.class, recommendation.getSourceStoragePool()); storagePools.add(storagePool); } return storagePools; } /** * Logs the recommendations for all VpoolUses. * @param recommendationMap - Map of VpoolUse to List<Recommendation> */ public void logRecommendations(Map<VpoolUse, List<Recommendation>> recommendationMap) { for (Map.Entry<VpoolUse, List<Recommendation>> entry : recommendationMap.entrySet()) { logRecommendations(entry.getKey().name(), entry.getValue()); } } public void logRecommendations(String label, List<Recommendation> recommendations) { _log.info("Recommendations for: " + label); for (Recommendation recommendation : recommendations) { logRecommendation(recommendation, 0); } } public void logRecommendation(Recommendation recommendation, int indent) { StringBuilder indentString = new StringBuilder(); for (int i = 0; i < indent; i++) { indentString.append(" ->"); } if (recommendation == null) { _log.info("null recommendations"); return; } indentString.substring(indentString.length() - indent); String type = recommendation.getClass().getSimpleName(); // some RP recommendations don't set varray, vpool, pool ... must be defensive String varrayLabel = "varray-unknown"; String vpoolLabel = "vpool-unknown"; String storagePoolLabel = "storagePool-unknown"; String systemLabel = "systemLabel-unknown"; if (recommendation.getVirtualArray() != null) { VirtualArray va = dbClient.queryObject(VirtualArray.class, recommendation.getVirtualArray()); varrayLabel = va.getLabel(); } if (recommendation.getVirtualPool() != null) { VirtualPool vp = recommendation.getVirtualPool(); vpoolLabel = vp.getLabel(); } if (recommendation.getSourceStoragePool() != null) { StoragePool pool = dbClient.queryObject(StoragePool.class, recommendation.getSourceStoragePool()); storagePoolLabel = pool.getLabel(); } if (recommendation.getSourceStorageSystem() != null) { StorageSystem sys = dbClient.queryObject(StorageSystem.class, recommendation.getSourceStorageSystem()); systemLabel = sys.getLabel(); } _log.info(String.format("%s%s va %s vp %s pool %s sys %s", indentString, type, varrayLabel, vpoolLabel, storagePoolLabel, systemLabel)); if (recommendation.getRecommendation() != null) { logRecommendation(recommendation.getRecommendation(), indent+1); } } public List<String> getSchedulerStack() { return schedulerStack; } public void setSchedulerStack(List<String> schedulerStack) { this.schedulerStack = schedulerStack; } }