use of com.emc.storageos.volumecontroller.Recommendation in project coprhd-controller by CoprHD.
the class RecoverPointScheduler method createRPProtectionRecommendationForMetroPoint.
/**
* Creates primary (active) and secondary (standby) cluster recommendations for MetroPoint.
*
* We first determine the type of MetroPoint request based on the protection virtual array
* configuration (single remote, local only, or local and remote). Using this information
* we determine a possible placement recommendation for the primary cluster. Using the
* primary cluster recommendation we then figure out a secondary cluster recommendation.
* The secondary cluster recommendation needs protection attributes that give with the
* primary cluster recommendation to satisfy the type of MetroPoint configuration requested.
*
* @param varray the source virtual array.
* @param protectionVarrays the RecoverPoint protection virtual arrays.
* @param vpool the source virtual pool.
* @param haVarray the HA virtual array - secondary cluster.
* @param haVpool the HA virtual pool - secondary cluster.
* @param capabilities parameters.
* @param candidateActiveSourcePools the candidate primary cluster source pools.
* @param candidateStandbySourcePools the candidate secondary cluster source pools.
* @param candidateProtectionPoolsMap pre-populated map for tgt varray to storage pools, use null if not needed
* @return list of Recommendation objects to satisfy the request
*/
private RPProtectionRecommendation createRPProtectionRecommendationForMetroPoint(VirtualArray varray, List<VirtualArray> protectionVarrays, VirtualPool vpool, VirtualArray haVarray, VirtualPool haVpool, VirtualPoolCapabilityValuesWrapper capabilities, List<StoragePool> candidateActiveSourcePools, List<StoragePool> candidateStandbySourcePools, Map<VirtualArray, List<StoragePool>> candidateProtectionPools, Volume vpoolChangeVolume, Project project) {
// Initialize a list of recommendations to be returned.
Set<ProtectionSystem> secondaryProtectionSystems = null;
placementStatus = new PlacementStatus();
secondaryPlacementStatus = new PlacementStatus();
int requestedResourceCount = capabilities.getResourceCount();
int totalSatisfiedCount = 0;
List<URI> protectionVarrayURIs = new ArrayList<URI>();
for (VirtualArray vArray : protectionVarrays) {
protectionVarrayURIs.add(vArray.getId());
placementStatus.getProcessedProtectionVArrays().put(vArray.getId(), false);
}
// Active journal varray - Either explicitly set by the user or use the default varray.
VirtualArray activeJournalVarray = (NullColumnValueGetter.isNotNullValue(vpool.getJournalVarray()) ? dbClient.queryObject(VirtualArray.class, URI.create(vpool.getJournalVarray())) : varray);
// Active journal vpool - Either explicitly set by the user or use the default vpool.
VirtualPool activeJournalVpool = (NullColumnValueGetter.isNotNullValue(vpool.getJournalVpool()) ? dbClient.queryObject(VirtualPool.class, URI.create(vpool.getJournalVpool())) : vpool);
// Standby journal varray - Either explicitly set by the user or use the default haVarray.
VirtualArray standbyJournalVarray = (NullColumnValueGetter.isNotNullValue(vpool.getStandbyJournalVarray()) ? dbClient.queryObject(VirtualArray.class, URI.create(vpool.getStandbyJournalVarray())) : haVarray);
// Standby journal vpool - Either explicitly set by the user or use the default haVpool.
VirtualPool standbyJournalVpool = (NullColumnValueGetter.isNotNullValue(vpool.getStandbyJournalVpool()) ? dbClient.queryObject(VirtualPool.class, URI.create(vpool.getStandbyJournalVpool())) : haVpool);
// Build the list of protection virtual arrays to consider for determining a
// primary placement solution. Add all virtual arrays from the source virtual
// pool list of protection virtual arrays, except for the HA/standby virtual array.
// In the case of local and/or remote protection, the HA virtual array should
// never be considered as a valid protection target for primary placement.
List<VirtualArray> activeProtectionVarrays = new ArrayList<VirtualArray>();
for (VirtualArray protectionVarray : protectionVarrays) {
if (!protectionVarray.getId().equals(haVarray.getId())) {
activeProtectionVarrays.add(protectionVarray);
}
}
// Build the list of protection virtual arrays to consider for determining a
// standby placement solution. Add all virtual arrays from the source virtual
// pool list of protection virtual arrays, except for the source virtual array.
// In the case of local and/or remote protection, the source virtual array should
// never be considered as a valid protection target for standby placement.
List<VirtualArray> standbyProtectionVarrays = new ArrayList<VirtualArray>();
for (VirtualArray protectionVarray : protectionVarrays) {
if (!protectionVarray.getId().equals(varray.getId())) {
standbyProtectionVarrays.add(protectionVarray);
}
}
// The attributes below will not change throughout the placement process
placementStatus.setSrcVArray(varray.getLabel());
placementStatus.setSrcVPool(vpool.getLabel());
boolean secondaryRecommendationSolution = false;
int satisfiedSourceVolCount = 0;
int totalRequestedResourceCount = capabilities.getResourceCount();
boolean isChangeVpool = (vpoolChangeVolume != null);
// Top level recommendation object
RPProtectionRecommendation rpProtectionRecommendation = new RPProtectionRecommendation();
// Source pool recommendations
List<Recommendation> sourcePoolRecommendations = new ArrayList<Recommendation>();
// Map to hold standby storage pools to protection systems
Map<URI, Set<ProtectionSystem>> standbyStoragePoolsToProtectionSystems = new HashMap<URI, Set<ProtectionSystem>>();
// Change vpool only: Set values for change vpool. If not a change vpool these values will be null.
rpProtectionRecommendation.setVpoolChangeVolume(vpoolChangeVolume != null ? vpoolChangeVolume.getId() : null);
rpProtectionRecommendation.setVpoolChangeNewVpool(vpoolChangeVolume != null ? vpool.getId() : null);
rpProtectionRecommendation.setVpoolChangeProtectionAlreadyExists(vpoolChangeVolume != null ? vpoolChangeVolume.checkForRp() : false);
// Change vpool only: Recommendation objects specifically used for change vpool. These will not be populated otherwise.
Recommendation changeVpoolSourceRecommendation = new Recommendation();
Recommendation changeVpoolStandbyRecommendation = new Recommendation();
if (isChangeVpool) {
// valid pool, the existing ones for active and standby. This is just to used to pass through the placement code.
if (null == vpoolChangeVolume.getAssociatedVolumes() || vpoolChangeVolume.getAssociatedVolumes().isEmpty()) {
_log.error("VPLEX volume {} has no backend volumes.", vpoolChangeVolume.forDisplay());
throw InternalServerErrorException.internalServerErrors.noAssociatedVolumesForVPLEXVolume(vpoolChangeVolume.forDisplay());
}
for (String associatedVolume : vpoolChangeVolume.getAssociatedVolumes()) {
Volume assocVol = dbClient.queryObject(Volume.class, URI.create(associatedVolume));
if (assocVol.getVirtualArray().equals(varray.getId())) {
// This is the existing active source backing volume
changeVpoolSourceRecommendation.setSourceStoragePool(assocVol.getPool());
StoragePool pool = dbClient.queryObject(StoragePool.class, assocVol.getPool());
changeVpoolSourceRecommendation.setSourceStorageSystem(pool.getStorageDevice());
changeVpoolSourceRecommendation.setResourceCount(1);
sourcePoolRecommendations.add(changeVpoolSourceRecommendation);
_log.info(String.format("RP Placement : Change Virtual Pool - Active source pool already exists, reuse pool: [%s] [%s].", pool.getLabel().toString(), pool.getId().toString()));
} else if (assocVol.getVirtualArray().equals(haVarray.getId())) {
// This is the existing standby source backing volume
changeVpoolStandbyRecommendation.setSourceStoragePool(assocVol.getPool());
StoragePool pool = dbClient.queryObject(StoragePool.class, assocVol.getPool());
changeVpoolStandbyRecommendation.setSourceStorageSystem(pool.getStorageDevice());
changeVpoolStandbyRecommendation.setResourceCount(1);
_log.info(String.format("RP Placement : Change Virtual Pool - Standby source pool already exists, reuse pool: [%s] [%s].", pool.getLabel().toString(), pool.getId().toString()));
}
}
satisfiedSourceVolCount = 1;
} else {
// If this is not a change vpool, then gather the recommended pools for the source.
sourcePoolRecommendations = getRecommendedPools(rpProtectionRecommendation, varray, vpool, null, null, capabilities, RPHelper.SOURCE, null);
}
// If we have no source pools at this point, throw an exception.
if (sourcePoolRecommendations == null || sourcePoolRecommendations.isEmpty()) {
_log.error(String.format("RP Placement : No matching storage pools found for the source varray: [%s. " + "There are no storage pools that match the passed vpool parameters and protocols and/or there are " + "no pools that have enough capacity to hold at least one resource of the requested size.", varray.getLabel()));
throw APIException.badRequests.noMatchingStoragePoolsForVpoolAndVarray(vpool.getLabel(), varray.getLabel());
}
_log.info(String.format("RP Placement : Determining RP placement for the primary (active) MetroPoint cluster for %s resources.", totalRequestedResourceCount));
// Keep track of the possible pools to use for the source. This will help us determine if we have
// exhausted all our options.
int remainingPossiblePrimarySrcPoolSolutions = sourcePoolRecommendations.size();
// Iterate over the source pools found to find a solution...
for (Recommendation recommendedPool : sourcePoolRecommendations) {
StoragePool sourcePool = dbClient.queryObject(StoragePool.class, recommendedPool.getSourceStoragePool());
--remainingPossiblePrimarySrcPoolSolutions;
satisfiedSourceVolCount = (recommendedPool.getResourceCount() >= requestedResourceCount) ? requestedResourceCount : recommendedPool.getResourceCount();
Set<ProtectionSystem> primaryProtectionSystems = new HashSet<ProtectionSystem>();
ProtectionSystem cgProtectionSystem = getCgProtectionSystem(capabilities.getBlockConsistencyGroup());
// used by other volumes in it.
if (cgProtectionSystem != null) {
BlockConsistencyGroup cg = dbClient.queryObject(BlockConsistencyGroup.class, capabilities.getBlockConsistencyGroup());
_log.info(String.format("RP Placement : Narrowing down placement to use protection system %s, which is currently used " + "by RecoverPoint consistency group %s.", cgProtectionSystem.getLabel(), cg));
primaryProtectionSystems.add(cgProtectionSystem);
} else {
primaryProtectionSystems = getProtectionSystemsForStoragePool(sourcePool, varray, true);
if (primaryProtectionSystems.isEmpty()) {
continue;
}
}
// Sort the ProtectionSystems based on the last time a CG was created. Always use the
// ProtectionSystem with the oldest cgLastCreated timestamp to support a round-robin
// style of load balancing.
List<ProtectionSystem> primaryProtectionSystemsList = sortProtectionSystems(primaryProtectionSystems);
for (ProtectionSystem primaryProtectionSystem : primaryProtectionSystemsList) {
Calendar cgLastCreated = primaryProtectionSystem.getCgLastCreatedTime();
_log.info(String.format("RP Placement : Attempting to use protection system [%s], which was last used to create a CG on [%s].", primaryProtectionSystem.getLabel(), cgLastCreated != null ? cgLastCreated.getTime().toString() : "N/A"));
// Get a list of associated storage systems for the pool, varray, and the protection system.
// This will return a list of strings that are in the format of:
// <storage system serial number>:<rp site name>
List<String> primaryAssociatedStorageSystems = getCandidateVisibleStorageSystems(sourcePool, primaryProtectionSystem, varray, activeProtectionVarrays, true);
if (primaryAssociatedStorageSystems.isEmpty()) {
// In this case no rp sites were connected to this storage system, we should not hit this,
// but just to be safe we'll catch it.
_log.info(String.format("RP Placement: Protection System %s does not have an rp site cluster connected to Storage pool %s ", primaryProtectionSystem.getLabel(), sourcePool.getLabel()));
continue;
}
// Iterate over the associated storage systems
for (String primaryAssociatedStorageSystem : primaryAssociatedStorageSystems) {
rpProtectionRecommendation.setProtectionDevice(primaryProtectionSystem.getId());
_log.info(String.format("RP Placement : Build MetroPoint Active Recommendation..."));
RPRecommendation sourceRec = buildSourceRecommendation(primaryAssociatedStorageSystem, varray, vpool, primaryProtectionSystem, sourcePool, capabilities, satisfiedSourceVolCount, placementStatus, vpoolChangeVolume, false);
if (sourceRec == null) {
// No source placement found for the primaryAssociatedStorageSystem, so continue.
_log.warn(String.format("RP Placement : Could not create MetroPoint Active Recommendation using [%s], continuing...", primaryAssociatedStorageSystem));
continue;
}
URI primarySourceStorageSystemURI = sourceRec.getVirtualVolumeRecommendation().getVPlexStorageSystem();
if (rpProtectionRecommendation.getSourceJournalRecommendation() == null) {
_log.info(String.format("RP Placement : Build MetroPoint Active Journal Recommendation..."));
RPRecommendation activeJournalRecommendation = buildJournalRecommendation(rpProtectionRecommendation, sourceRec.getInternalSiteName(), vpool.getJournalSize(), activeJournalVarray, activeJournalVpool, primaryProtectionSystem, capabilities, totalRequestedResourceCount, vpoolChangeVolume, false);
if (activeJournalRecommendation == null) {
// No source journal placement found, so continue.
_log.warn(String.format("RP Placement : Could not create MetroPoint Active Journal Recommendation, continuing..."));
continue;
}
rpProtectionRecommendation.setSourceJournalRecommendation(activeJournalRecommendation);
}
rpProtectionRecommendation.getSourceRecommendations().add(sourceRec);
_log.info("RP Placement : An RP source placement solution has been identified for the MetroPoint primary (active) cluster.");
// Find a solution, given this vpool, and the target varrays
if (findSolution(rpProtectionRecommendation, sourceRec, varray, vpool, activeProtectionVarrays, capabilities, satisfiedSourceVolCount, true, null, project)) {
_log.info("RP Placement : An RP target placement solution has been identified for the MetroPoint primary (active) cluster.");
// We have a primary cluster protection recommendation for the specified metroPointType. We need to now determine if
// we can protect the secondary cluster for the given metroPointType.
_log.info("RP Placement : Determining RP placement for the secondary (standby) MetroPoint cluster.");
secondaryRecommendationSolution = false;
// Get the candidate secondary cluster source pools - sets secondarySourcePoolURIs.
List<Recommendation> secondaryPoolsRecommendation = new ArrayList<Recommendation>();
if (isChangeVpool) {
secondaryPoolsRecommendation.add(changeVpoolStandbyRecommendation);
} else {
secondaryPoolsRecommendation = getRecommendedPools(rpProtectionRecommendation, haVarray, haVpool, null, null, capabilities, RPHelper.TARGET, null);
}
secondaryPlacementStatus.setSrcVArray(haVarray.getLabel());
secondaryPlacementStatus.setSrcVPool(haVpool.getLabel());
for (Recommendation secondaryPoolRecommendation : secondaryPoolsRecommendation) {
// Start with the top of the list of source pools, find a solution based on that.
StoragePool standbySourcePool = dbClient.queryObject(StoragePool.class, secondaryPoolRecommendation.getSourceStoragePool());
// Lookup source pool protection systems in the cache first.
if (standbyStoragePoolsToProtectionSystems.containsKey(standbySourcePool.getId())) {
secondaryProtectionSystems = standbyStoragePoolsToProtectionSystems.get(standbySourcePool.getId());
} else {
secondaryProtectionSystems = getProtectionSystemsForStoragePool(standbySourcePool, haVarray, true);
if (secondaryProtectionSystems.isEmpty()) {
continue;
}
// Cache the result for this pool
standbyStoragePoolsToProtectionSystems.put(standbySourcePool.getId(), secondaryProtectionSystems);
}
ProtectionSystem selectedSecondaryProtectionSystem = null;
// Ensure the we have a secondary protection system that matches the primary protection system
for (ProtectionSystem secondaryProtectionSystem : secondaryProtectionSystems) {
if (secondaryProtectionSystem.getId().equals(rpProtectionRecommendation.getProtectionDevice())) {
// We have a protection system match for this pool, continue.
selectedSecondaryProtectionSystem = secondaryProtectionSystem;
break;
}
}
if (selectedSecondaryProtectionSystem == null) {
// There is no protection system for this pool that matches the selected primary
// protection system. So lets try another pool.
_log.info(String.format("RP Placement: Secondary source storage pool %s " + " does not have connectivity to the selected primary protection system.", standbySourcePool.getLabel()));
continue;
} else {
// List of concatenated strings that contain the RP site + associated storage system.
List<String> secondaryAssociatedStorageSystems = getCandidateVisibleStorageSystems(standbySourcePool, selectedSecondaryProtectionSystem, haVarray, activeProtectionVarrays, true);
// make sure you check RP topology to see if the sites can protect that many targets
if (secondaryAssociatedStorageSystems.isEmpty()) {
// no rp site clusters connected to this storage system, should not hit this,
// but just to be safe we'll catch it
_log.info("RP Placement: Protection System " + selectedSecondaryProtectionSystem.getLabel() + " does not have an rp site cluster connected to Storage pool " + standbySourcePool.getLabel());
continue;
}
Set<String> validSecondaryAssociatedStorageSystems = new LinkedHashSet<String>();
// that reference the same storage system as the primary recommendation and has a different RP site.
for (String secondaryAssociatedStorageSystem : secondaryAssociatedStorageSystems) {
String secondarySourceInternalSiteName = ProtectionSystem.getAssociatedStorageSystemSiteName(secondaryAssociatedStorageSystem);
URI secondarySourceStorageSystemURI = ConnectivityUtil.findStorageSystemBySerialNumber(ProtectionSystem.getAssociatedStorageSystemSerialNumber(secondaryAssociatedStorageSystem), dbClient, StorageSystemType.BLOCK);
boolean validForStandbyPlacement = false;
if (secondarySourceStorageSystemURI.equals(primarySourceStorageSystemURI) && !secondarySourceInternalSiteName.equals(sourceRec.getInternalSiteName())) {
validForStandbyPlacement = true;
validSecondaryAssociatedStorageSystems.add(secondaryAssociatedStorageSystem);
}
_log.info(String.format("RP Placement: associated storage system entry [%s] " + "%s valid for standby placement.", secondaryAssociatedStorageSystem, (validForStandbyPlacement ? "" : "NOT")));
}
for (String secondaryAssociatedStorageSystem : validSecondaryAssociatedStorageSystems) {
_log.info(String.format("RP Placement : Build MetroPoint Standby Recommendation..."));
RPRecommendation secondaryRpRecommendation = buildSourceRecommendation(secondaryAssociatedStorageSystem, haVarray, haVpool, selectedSecondaryProtectionSystem, standbySourcePool, capabilities, satisfiedSourceVolCount, secondaryPlacementStatus, null, true);
if (secondaryRpRecommendation == null) {
// No standby placement found for the secondaryAssociatedStorageSystem, so continue.
_log.warn(String.format("RP Placement : Could not create MetroPoint Standby Recommendation using [%s], continuing...", secondaryAssociatedStorageSystem));
continue;
}
if (rpProtectionRecommendation.getStandbyJournalRecommendation() == null) {
_log.info(String.format("RP Placement : Build MetroPoint Standby Journal Recommendation..."));
RPRecommendation standbyJournalRecommendation = buildJournalRecommendation(rpProtectionRecommendation, secondaryRpRecommendation.getInternalSiteName(), vpool.getJournalSize(), standbyJournalVarray, standbyJournalVpool, primaryProtectionSystem, capabilities, totalRequestedResourceCount, vpoolChangeVolume, true);
if (standbyJournalRecommendation == null) {
// No standby journal placement found, so continue.
_log.warn(String.format("RP Placement : Could not create MetroPoint Standby Journal Recommendation, continuing..."));
continue;
}
rpProtectionRecommendation.setStandbyJournalRecommendation(standbyJournalRecommendation);
}
sourceRec.setHaRecommendation(secondaryRpRecommendation);
// Find a solution, given this vpool, and the target varrays
if (findSolution(rpProtectionRecommendation, secondaryRpRecommendation, haVarray, vpool, standbyProtectionVarrays, capabilities, satisfiedSourceVolCount, true, sourceRec, project)) {
_log.info("RP Placement : An RP target placement solution has been identified for the " + "MetroPoint secondary (standby) cluster.");
secondaryRecommendationSolution = true;
break;
} else {
_log.info("RP Placement : Unable to find a suitable solution, continuining to find other solutions.");
continue;
}
}
if (secondaryRecommendationSolution) {
break;
} else {
continue;
}
}
}
if (!secondaryRecommendationSolution) {
_log.info("RP Placement : Unable to find MetroPoint secondary cluster placement recommendation that " + "jives with primary cluster recommendation. Need to find a new primary recommendation.");
// Exhausted all the secondary pool URIs. Need to find another primary solution.
break;
}
// We are done - secondary recommendation found
requestedResourceCount = requestedResourceCount - satisfiedSourceVolCount;
totalSatisfiedCount += satisfiedSourceVolCount;
if (totalSatisfiedCount >= totalRequestedResourceCount) {
rpProtectionRecommendation.setResourceCount(totalSatisfiedCount);
// Check to ensure the protection system can handle the new resources about to come down
if (!verifyPlacement(primaryProtectionSystem, rpProtectionRecommendation, rpProtectionRecommendation.getResourceCount())) {
continue;
}
return rpProtectionRecommendation;
} else {
// loop back to the next pool
break;
}
} else {
// Not sure there's anything to do here. Just go to the next candidate protection system or Protection System
_log.info(String.format("RP Placement : Could not find a solution against protection system %s and internal " + "cluster name %s", primaryProtectionSystem.getLabel(), sourceRec.getInternalSiteName()));
rpProtectionRecommendation = getNewProtectionRecommendation(vpoolChangeVolume, vpool);
}
}
// end of for loop trying to find solution using possible rp cluster sites
rpProtectionRecommendation = getNewProtectionRecommendation(vpoolChangeVolume, vpool);
}
// end of protection systems for loop
}
// a solution
if ((remainingPossiblePrimarySrcPoolSolutions == 0) && totalSatisfiedCount < capabilities.getResourceCount()) {
_log.error("Could not find a MetroPoint placement solution. In a MetroPoint consistency group, there can " + "exist at most one remote copy and from zero to two local copies. If there is no remote copy, " + "there must be two local copies, one at each side of the VPLEX Metro.");
throw APIException.badRequests.cannotFindSolutionForRP(buildMetroProintPlacementStatusString());
}
_log.error("ViPR could not find matching target storage pools that could be protected via RecoverPoint");
_log.error("Could not find a MetroPoint placement solution. In a MetroPoint consistency group, there can " + "exist at most one remote copy and from zero to two local copies. If there is no remote copy, " + "there must be two local copies, one at each side of the VPLEX Metro.");
throw APIException.badRequests.cannotFindSolutionForRP(buildMetroProintPlacementStatusString());
}
use of com.emc.storageos.volumecontroller.Recommendation in project coprhd-controller by CoprHD.
the class RecoverPointScheduler method getAllHARecommendations.
/**
* Gets all the HA placement recommendations.
*
* @param srcVarray The source virtual array
* @param srcVpool The source virtual pool
* @param requestedHaVarray The requested highly available virtual array
* @param haVpool The highly available virtual pool
* @param capabilities The virtual pool capabilities
* @param vplexPoolMapForSrcVarray The source virtual array, VPlex connected storage pools
* @param srcStorageSystem The selected VPlex source leg storage system
* @param isRpTarget true if the request is specific to a RecoverPoint target, false otherwise
*
* @return A list of VPlexRecommendation instances specifying the
* HA recommended resource placement resources
*/
protected List<Recommendation> getAllHARecommendations(VirtualArray srcVarray, VirtualPool srcVpool, VirtualArray requestedHaVarray, VirtualPool haVpool, VirtualPoolCapabilityValuesWrapper capabilities, Map<String, List<StoragePool>> vplexPoolMapForSrcVarray) {
_log.info("Executing VPlex high availability placement strategy");
// Initialize the list of recommendations.
List<Recommendation> recommendations = new ArrayList<Recommendation>();
// The list of potential VPlex storage systems.
Set<String> vplexStorageSystemIds = vplexPoolMapForSrcVarray.keySet();
_log.info(String.format("%s VPlex storage systems have matching pools", vplexStorageSystemIds.size()));
// For an HA request, get the possible high availability varrays
// for each potential VPlex storage system.
Map<String, List<String>> vplexHaVarrayMap = ConnectivityUtil.getVPlexVarrays(dbClient, vplexStorageSystemIds, srcVarray.getId());
// passed VirtualPool is use.
if (haVpool == null) {
haVpool = srcVpool;
}
_log.info(String.format("Requested HA varray is %s", (requestedHaVarray != null ? requestedHaVarray.getId() : "not specified")));
// Loop over the potential VPlex storage systems, and attempt
// to place the resources.
Iterator<String> vplexSystemIdsIter = vplexStorageSystemIds.iterator();
while (vplexSystemIdsIter.hasNext()) {
String vplexStorageSystemId = vplexSystemIdsIter.next();
_log.info(String.format("Check matching pools for VPlex %s", vplexStorageSystemId));
// pools for this VPlex storage system.
if (VirtualPool.ProvisioningType.Thin.toString().equalsIgnoreCase(srcVpool.getSupportedProvisioningType())) {
capabilities.put(VirtualPoolCapabilityValuesWrapper.THIN_PROVISIONING, Boolean.TRUE);
}
// Otherwise we now have to see if there is an HA varray
// for the VPlex that also contains pools suitable to place
// the resources.
List<String> vplexHaVarrayIds = vplexHaVarrayMap.get(vplexStorageSystemId);
_log.info(String.format("Found %s HA varrays", vplexHaVarrayIds.size()));
for (String vplexHaVarrayId : vplexHaVarrayIds) {
_log.info(String.format("Check HA varray %s", vplexHaVarrayId));
// varray is not it, then skip the varray.
if ((requestedHaVarray != null) && (!vplexHaVarrayId.equals(requestedHaVarray.getId().toString()))) {
_log.info("Not the requested HA varray, skip");
continue;
}
// Get all storage pools that match the passed VirtualPool params,
// and this HA VirtualArray. In addition, the
// pool must have enough capacity to hold at least one
// resource of the requested size.
VirtualArray vplexHaVarray = dbClient.queryObject(VirtualArray.class, URI.create(vplexHaVarrayId));
Map<String, Object> attributeMap = new HashMap<String, Object>();
List<StoragePool> allMatchingPoolsForHaVarray = vplexScheduler.getMatchingPools(vplexHaVarray, null, haVpool, capabilities, attributeMap);
_log.info(String.format("Found %s matching pools for HA varray", allMatchingPoolsForHaVarray.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>> vplexPoolMapForHaVarray = vplexScheduler.sortPoolsByVPlexStorageSystem(allMatchingPoolsForHaVarray, vplexHaVarrayId);
// 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> recommendationsForHaVarray = new ArrayList<Recommendation>();
if (vplexPoolMapForHaVarray.containsKey(vplexStorageSystemId)) {
_log.info(String.format("Found matching pools in HA varray for VPlex %s", vplexStorageSystemId));
if (VirtualPool.ProvisioningType.Thin.toString().equalsIgnoreCase(haVpool.getSupportedProvisioningType())) {
capabilities.put(VirtualPoolCapabilityValuesWrapper.THIN_PROVISIONING, Boolean.TRUE);
}
recommendationsForHaVarray = blockScheduler.getRecommendationsForPools(vplexHaVarray.getId().toString(), vplexPoolMapForHaVarray.get(vplexStorageSystemId), capabilities);
} else {
_log.info(String.format("No matching pools in HA varray for VPlex %s", vplexStorageSystemId));
}
// the source and HA varrays.
if (!recommendationsForHaVarray.isEmpty()) {
_log.info("Matching pools in HA varray sufficient for placement.");
recommendations.addAll(vplexScheduler.createVPlexRecommendations(vplexStorageSystemId, vplexHaVarray, haVpool, recommendationsForHaVarray));
}
// varrays for this VPlex.
if (!recommendations.isEmpty() || (requestedHaVarray != null)) {
_log.info("Done trying to place resource for VPlex.");
break;
}
}
}
return recommendations;
}
use of com.emc.storageos.volumecontroller.Recommendation in project coprhd-controller by CoprHD.
the class RecoverPointScheduler method buildCgRecommendations.
/**
* Builds a recommendation from existing CG.
*
* This method is called when adding more volumes into an existing CG or change vpool scenario.
*
* When adding to an existing CG we can accommodate the request with the recommendations from
* resources that have already been placed in the existing CG.
*
* @param capabilities - Virtual Pool capabilities
* @param vpool - Virtual Pool
* @param protectionVarrays - List of target copy virtual arrays
* @param vpoolChangeVolume - change virtual pool volume
* @return - List of recommendations
*/
protected List<Recommendation> buildCgRecommendations(VirtualPoolCapabilityValuesWrapper capabilities, VirtualPool vpool, List<VirtualArray> protectionVarrays, Volume vpoolChangeVolume) {
BlockConsistencyGroup cg = dbClient.queryObject(BlockConsistencyGroup.class, capabilities.getBlockConsistencyGroup());
_log.info(String.format("Attempting to align placement (protection system, storage pools, internal site names) with " + "existing volumes in RecoverPoint consistency group %s.", cg.getLabel()));
List<Recommendation> recommendations = new ArrayList<Recommendation>();
// Find the first existing source volume
List<Volume> sourceVolumes = RPHelper.getCgSourceVolumes(cg.getId(), dbClient);
if (sourceVolumes.isEmpty()) {
_log.info(String.format("Unable to fully align placement with existing volumes in RecoverPoint consistency group %s. " + "The consistency group currently contains no volumes.", cg.getLabel()));
return recommendations;
}
// Verify that all the underlying protection storage pools used by the existing source volume are available to this request
if (!verifyExistingSourceProtectionPools(sourceVolumes.get(0), vpool, cg.getLabel())) {
return recommendations;
}
Volume sourceVolume = null;
boolean createRecommendations = false;
for (Volume currentSourceVolume : sourceVolumes) {
// enough capacity, use it to produce the recommendation.
if (cgPoolsHaveAvailableCapacity(currentSourceVolume, capabilities, vpool, protectionVarrays)) {
createRecommendations = true;
sourceVolume = currentSourceVolume;
break;
}
}
if (!createRecommendations) {
return recommendations;
}
RPProtectionRecommendation recommendation = new RPProtectionRecommendation();
if (sourceVolume.getProtectionController() != null) {
ProtectionSystem ps = dbClient.queryObject(ProtectionSystem.class, sourceVolume.getProtectionController());
if (ps.getInactive()) {
// be protected so we must fail.
throw APIException.badRequests.cgReferencesInvalidProtectionSystem(cg.getId(), sourceVolume.getProtectionController());
}
} else {
// be protected so we must fail.
throw APIException.badRequests.cgReferencesInvalidProtectionSystem(cg.getId(), sourceVolume.getProtectionController());
}
recommendation.setProtectionDevice(sourceVolume.getProtectionController());
recommendation.setVpoolChangeVolume(vpoolChangeVolume != null ? vpoolChangeVolume.getId() : null);
recommendation.setVpoolChangeNewVpool(vpoolChangeVolume != null ? vpool.getId() : null);
recommendation.setVpoolChangeProtectionAlreadyExists(vpoolChangeVolume != null ? vpoolChangeVolume.checkForRp() : false);
recommendation.setResourceCount(capabilities.getResourceCount());
// Check to see if we need an additional journal for Source
Map<Integer, Long> additionalJournalForSource = RPHelper.additionalJournalRequiredForRPCopy(vpool.getJournalSize(), cg.getId(), capabilities.getSize(), capabilities.getResourceCount(), sourceVolume.getRpCopyName(), dbClient);
if (!CollectionUtils.isEmpty(additionalJournalForSource)) {
// ACTIVE SOURCE JOURNAL Recommendation
List<Volume> sourceJournals = RPHelper.findExistingJournalsForCopy(dbClient, sourceVolume.getConsistencyGroup(), sourceVolume.getRpCopyName());
Volume sourceJournal = sourceJournals.get(0);
if (sourceJournal == null) {
_log.error(String.format("No existing source journal found in CG [%s] for copy [%s], returning false", sourceVolume.getConsistencyGroup(), sourceVolume.getRpCopyName()));
throw APIException.badRequests.unableToFindSuitableJournalRecommendation();
}
VirtualPool sourceJournalVpool = NullColumnValueGetter.isNotNullValue(vpool.getJournalVpool()) ? dbClient.queryObject(VirtualPool.class, URI.create(vpool.getJournalVpool())) : vpool;
Long sourceJournalSize = getJournalCapabilities(vpool.getJournalSize(), capabilities, 1).getSize();
RPRecommendation sourceJournalRecommendation = buildRpRecommendationFromExistingVolume(sourceJournal, sourceJournalVpool, capabilities, sourceJournalSize);
// Parse out the calculated values
Map.Entry<Integer, Long> entry = additionalJournalForSource.entrySet().iterator().next();
Integer journalCount = entry.getKey();
Long journalSize = entry.getValue();
// Override values in recommendation with calculated journal count and size
sourceJournalRecommendation.setResourceCount(journalCount);
sourceJournalRecommendation.setSize(journalSize);
recommendation.setSourceJournalRecommendation(sourceJournalRecommendation);
// STANDBY SOURCE JOURNAL Recommendation
String standbyCopyName = RPHelper.getStandbyProductionCopyName(dbClient, sourceVolume);
if (standbyCopyName != null) {
List<Volume> existingStandbyJournals = RPHelper.findExistingJournalsForCopy(dbClient, sourceVolume.getConsistencyGroup(), standbyCopyName);
Volume standbyJournal = existingStandbyJournals.get(0);
if (standbyJournal == null) {
_log.error(String.format("No existing standby journal found in CG [%s] for copy [%s], returning false", sourceVolume.getConsistencyGroup(), standbyCopyName));
throw APIException.badRequests.unableToFindSuitableJournalRecommendation();
}
VirtualPool haVpool = (null != VirtualPool.getHAVPool(vpool, dbClient)) ? VirtualPool.getHAVPool(vpool, dbClient) : vpool;
VirtualPool standbyJournalVpool = NullColumnValueGetter.isNotNullValue(vpool.getStandbyJournalVpool()) ? dbClient.queryObject(VirtualPool.class, URI.create(vpool.getStandbyJournalVpool())) : haVpool;
RPRecommendation standbyJournalRecommendation = buildRpRecommendationFromExistingVolume(standbyJournal, standbyJournalVpool, capabilities, sourceJournalSize);
// Override values in recommendation with calculated journal count and size
standbyJournalRecommendation.setResourceCount(journalCount);
standbyJournalRecommendation.setSize(journalSize);
recommendation.setStandbyJournalRecommendation(standbyJournalRecommendation);
}
}
// SOURCE Recommendation
RPRecommendation sourceRecommendation = buildRpRecommendationFromExistingVolume(sourceVolume, vpool, capabilities, null);
recommendation.getSourceRecommendations().add(sourceRecommendation);
// TARGET Recommendation(s)
Map<URI, VpoolProtectionVarraySettings> protectionSettings = VirtualPool.getProtectionSettings(vpool, dbClient);
for (VirtualArray protectionVarray : protectionVarrays) {
Volume targetVolume = getTargetVolumeForProtectionVirtualArray(sourceVolume, protectionVarray);
// if the target vpool is not set, it defaults to the source vpool
VirtualPool targetVpool = vpool;
if (protectionSettings.get(protectionVarray.getId()) != null && protectionSettings.get(protectionVarray.getId()).getVirtualPool() != null) {
targetVpool = dbClient.queryObject(VirtualPool.class, protectionSettings.get(protectionVarray.getId()).getVirtualPool());
}
RPRecommendation targetRecommendation = buildRpRecommendationFromExistingVolume(targetVolume, targetVpool, capabilities, null);
if (sourceRecommendation.getTargetRecommendations() == null) {
sourceRecommendation.setTargetRecommendations(new ArrayList<RPRecommendation>());
}
sourceRecommendation.getTargetRecommendations().add(targetRecommendation);
// Check to see if we need an additional journal for Target
Map<Integer, Long> additionalJournalForTarget = RPHelper.additionalJournalRequiredForRPCopy(vpool.getJournalSize(), cg.getId(), capabilities.getSize(), capabilities.getResourceCount(), targetVolume.getRpCopyName(), dbClient);
if (!CollectionUtils.isEmpty(additionalJournalForTarget)) {
// TARGET JOURNAL Recommendation
List<Volume> targetJournals = RPHelper.findExistingJournalsForCopy(dbClient, targetVolume.getConsistencyGroup(), targetVolume.getRpCopyName());
Volume targetJournal = targetJournals.get(0);
if (targetJournal == null) {
_log.error(String.format("No existing target journal found in CG [%s] for copy [%s], returning false", targetVolume.getConsistencyGroup(), targetVolume.getRpCopyName()));
throw APIException.badRequests.unableToFindSuitableJournalRecommendation();
}
VirtualPool targetJournalVpool = protectionSettings.get(protectionVarray.getId()).getJournalVpool() != null ? dbClient.queryObject(VirtualPool.class, protectionSettings.get(protectionVarray.getId()).getJournalVpool()) : targetVpool;
Long targetJournalSize = getJournalCapabilities(protectionSettings.get(protectionVarray.getId()).getJournalSize(), capabilities, 1).getSize();
RPRecommendation targetJournalRecommendation = buildRpRecommendationFromExistingVolume(targetJournal, targetJournalVpool, capabilities, targetJournalSize);
// Parse out the calculated values
Map.Entry<Integer, Long> entry = additionalJournalForSource.entrySet().iterator().next();
Integer journalCount = entry.getKey();
Long journalSize = entry.getValue();
// Override values in recommendation with calculated journal count and size
targetJournalRecommendation.setResourceCount(journalCount);
targetJournalRecommendation.setSize(journalSize);
if (recommendation.getTargetJournalRecommendations() == null) {
recommendation.setTargetJournalRecommendations(new ArrayList<RPRecommendation>());
}
recommendation.getTargetJournalRecommendations().add(targetJournalRecommendation);
}
}
_log.info(String.format("Produced recommendations based on existing source volume [%s](%s) from " + "RecoverPoint consistency group [%s].", sourceVolume.getLabel(), sourceVolume.getId(), cg.getLabel()));
recommendations.add(recommendation);
return recommendations;
}
use of com.emc.storageos.volumecontroller.Recommendation in project coprhd-controller by CoprHD.
the class StorageScheduler method performArrayAffinityPlacement.
/**
* Try to determine a list of storage pools from the passed list of storage
* pools that can accommodate the passed number of resources of the passed
* size accord to array affinity policy.
*
* @param varrayId String of VirtualArray ID for the recommendations.
* @param capabilities VirtualPoolCapabilityValuesWrapper.
* @param candidatePools The list of candidate storage pools.
* @param inCG In a Consistency Group
*
* @return The list of Recommendation instances reflecting the recommended
* pools.
*/
private List<Recommendation> performArrayAffinityPlacement(String varrayId, VirtualPoolCapabilityValuesWrapper capabilities, List<StoragePool> candidatePools, boolean inCG) {
Map<URI, Double> arrayToHostWeightMap = new HashMap<URI, Double>();
Map<URI, Set<URI>> preferredPoolMap = null;
boolean canUseNonPreferred = false;
if (capabilities.getArrayAffinity()) {
String computeIdStr = capabilities.getCompute();
preferredPoolMap = getPreferredPoolMap(computeIdStr, arrayToHostWeightMap);
_log.info("ArrayAffinity - preferred arrays for {} - {}", computeIdStr, arrayToHostWeightMap);
int limit = Integer.valueOf(_customConfigHandler.getComputedCustomConfigValue(CustomConfigConstants.HOST_RESOURCE_MAX_NUM_OF_ARRAYS, CustomConfigConstants.GLOBAL_KEY, null));
canUseNonPreferred = preferredPoolMap.keySet().size() < limit;
} else {
preferredPoolMap = new HashMap<URI, Set<URI>>();
canUseNonPreferred = true;
}
_log.info("ArrayAffinity - allow non preferred array {}", canUseNonPreferred);
// group pools by array
Map<URI, List<StoragePool>> candidatePoolMap = groupPoolsByArray(candidatePools, canUseNonPreferred, arrayToHostWeightMap.keySet());
if (candidatePoolMap == null || candidatePoolMap.isEmpty()) {
throw APIException.badRequests.noCandidateStoragePoolsForArrayAffinity();
}
// get all the candidate arrays
List<StorageSystem> candidateSystems = _dbClient.queryObject(StorageSystem.class, candidatePoolMap.keySet());
// all pools that can be used for placement
List<StoragePool> poolList = new ArrayList<StoragePool>();
for (List<StoragePool> pools : candidatePoolMap.values()) {
poolList.addAll(pools);
}
// compute and set storage pools' and arrays' average port usage metrics before sorting
_log.info("ArrayAffinity - compute port metrics");
Map<URI, Double> arrayToAvgPortMetricsMap = _portMetricsProcessor.computeStoragePoolsAvgPortMetrics(poolList);
// sort the arrays, first by host/cluster's preference, then by array's average port metrics
// then by free capacity and capacity utilization
Collections.sort(candidateSystems, new StorageSystemArrayAffinityComparator(arrayToHostWeightMap, candidatePoolMap, arrayToAvgPortMetricsMap));
_log.info("ArrayAffinity - sorted candidate systems {}", Joiner.on(',').join(Collections2.transform(candidateSystems, CommonTransformerFunctions.fctnDataObjectToID())));
// process the sorted candidate arrays
for (StorageSystem system : candidateSystems) {
URI systemURI = system.getId();
// get all available pools of the array
List<StoragePool> availablePools = candidatePoolMap.get(system.getId());
// sort the pools by free capacity, and capacity utilization
StoragePoolCapacityComparator poolComparator = new StoragePoolCapacityComparator();
Collections.sort(availablePools, poolComparator);
// split the pools into two lists by preference, both are sorted
Set<URI> preferredPoolURIs = preferredPoolMap.get(systemURI);
List<StoragePool> preferredPools = new ArrayList<StoragePool>();
List<StoragePool> nonPreferredPools = new ArrayList<StoragePool>();
for (StoragePool pool : availablePools) {
if (preferredPoolURIs != null && preferredPoolURIs.contains(pool.getId())) {
preferredPools.add(pool);
} else {
nonPreferredPools.add(pool);
}
}
// create a list of secondary pools (preferred and non preferred)
// the list is sorted by preference first, then by capacity
// the list will be used only if preferred pools along cannot satisfy the request
//
// IBM XIV, all volumes in a CG must belong to same pool
List<StoragePool> secondaryPools = new ArrayList<StoragePool>();
// for IBM XIV, all volumes in a CG must be in same pool
if (inCG && Type.ibmxiv.name().equals(system.getSystemType())) {
if (preferredPools.isEmpty()) {
if (!nonPreferredPools.isEmpty()) {
// only keep first non preferred pool
secondaryPools.add(nonPreferredPools.get(0));
}
} else {
// only keep first preferred pool
preferredPools = Arrays.asList(preferredPools.get(0));
// only keep the first non preferred pool if it has more capacity than the preferred one
if (!nonPreferredPools.isEmpty() && poolComparator.compare(nonPreferredPools.get(0), preferredPools.get(0)) < 0) {
secondaryPools.add(nonPreferredPools.get(0));
}
}
} else {
// only if there is a non preferred pool
if (!nonPreferredPools.isEmpty()) {
secondaryPools.addAll(preferredPools);
secondaryPools.addAll(nonPreferredPools);
}
}
// start from preferredPools
if (!preferredPools.isEmpty()) {
_log.info("ArrayAffinity - preferred pools {}", Joiner.on(',').join(preferredPools));
List<Recommendation> recommendations = getRecommendedPools(varrayId, preferredPools, capabilities, false);
if (!recommendations.isEmpty()) {
return recommendations;
} else {
_log.info("ArrayAffinity - no recommended pools found from perferred pools");
}
}
// then secondaryPools
if (!secondaryPools.isEmpty()) {
_log.info("ArrayAffinity - secondary pools {}", Joiner.on(',').join(secondaryPools));
// send both preferred and non preferred pools for the system
List<Recommendation> recommendations = getRecommendedPools(varrayId, availablePools, capabilities, false);
if (!recommendations.isEmpty()) {
return recommendations;
} else {
_log.info("ArrayAffinity - no recommended pools found from secondary pools");
}
}
}
// No recommendations found on any storage system.
return new ArrayList<Recommendation>();
}
use of com.emc.storageos.volumecontroller.Recommendation in project coprhd-controller by CoprHD.
the class StorageScheduler method getRecommendationsForPools.
/**
* Try to determine a list of storage pools from the passed list of storage
* pools that can accommodate the passed number of resources of the passed
* size.
*
* @param varrayId The VirtualArray for the recommendations.
* @param candidatePools The list of candidate storage pools.
*
* @return The list of Recommendation instances reflecting the recommended
* pools.
*/
protected List<Recommendation> getRecommendationsForPools(String varrayId, List<StoragePool> candidatePools, VirtualPoolCapabilityValuesWrapper capabilities) {
// If the capabilities specify a CG and the CG has yet to actually be
// created on a physical device, we need to make sure that the call
// returns recommended storage pools that are all on the same storage
// system. If the CG is created, the passed list of candidate pools
// will already filtered to that storage system. However, when it is
// not created, the candidate pools are all pools on all systems that
// / satisfy the placement criteria. Also, if only one volume is being
// provisioned, it becomes irrelevant as only one storage pool will
// be recommended.
URI cgURI = capabilities.getBlockConsistencyGroup();
int count = capabilities.getResourceCount();
boolean inCG = false;
if ((count > 1) && (!NullColumnValueGetter.isNullURI(cgURI))) {
BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, cgURI);
if (cg == null) {
throw APIException.internalServerErrors.invalidObject(cgURI.toString());
}
if (!cg.created()) {
// don't know ConsistencyGroup's StorageSystem type yet
inCG = true;
}
}
// Only difference is that resources will not be placed to more than one preferred systems if inCG is true
if (capabilities.getResourceCount() > 1 && inCG || capabilities.getArrayAffinity()) {
_log.info("Calling performArrayAffinityPlacement");
List<Recommendation> recommendations = performArrayAffinityPlacement(varrayId, capabilities, candidatePools, inCG);
if (!recommendations.isEmpty()) {
return recommendations;
} else {
// No recommendations found on any storage system.
return new ArrayList<Recommendation>();
}
}
// this is the behavior without array affinity policy
_log.info("Calling getRecommendedPools");
return getRecommendedPools(varrayId, candidatePools, capabilities, true);
}
Aggregations