/*
* Copyright (c) 2016 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.block;
import static com.emc.storageos.db.client.util.CommonTransformerFunctions.collectionString;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.model.ExportMask;
import com.emc.storageos.db.client.model.Volume;
import com.google.common.collect.Sets;
/**
* Utility class for ExportMaskPlacementDescriptor. Class should have higher-order functionality
* than the basic ExportMaskPlacementDescriptor manipulators. It's also good to have this class,
* so that it can be invoked for unit testing.
*/
public final class ExportMaskPlacementDescriptorHelper {
private static final Logger log = LoggerFactory.getLogger(ExportMaskPlacementDescriptorHelper.class);
/**
* This is intended to be a utility class, so make the constructor private
*/
private ExportMaskPlacementDescriptorHelper() {
}
/**
* If there are any invalidMasks, try to determine if we can use alternative and equivalent ExportMasks.
* If so, we will use them for placement.
*
* @param descriptor [IN/OUT] - Context used for backend placement
*
*/
public static void putUnplacedVolumesIntoAlternativeMask(ExportMaskPlacementDescriptor descriptor) {
// First check: are there any unplaced volumes?
if (!descriptor.hasUnPlacedVolumes()) {
// Nope - nothing to do here.
return;
}
log.info("Trying to see if there are any alternative exports that can be used for unplaced volumes...");
// All the volumes to be placed: need this to look up the Volume object
Map<URI, Volume> volumes = descriptor.getVolumesToPlace();
// The replacement map: this represents a remapping of the volume
// to equivalent, alternative ExportMasks
Map<URI, Map<URI, Volume>> replaceMap = new HashMap<>();
// Go through all the unplaced volumes ...
for (URI volumeURI : descriptor.getUnplacedVolumes().keySet()) {
// Get the set of ExportMasks that are equivalent, but perhaps not originally placed for the volume.
Set<URI> equivalentExportsForVolume = descriptor.getAlternativeExportsForVolume(volumeURI);
log.info("Found these ExportMasks are alternatives for volume {}: {}", volumeURI,
collectionString(equivalentExportsForVolume));
if (!equivalentExportsForVolume.isEmpty()) {
// Find the ExportMask with the least number of volumes
URI selectedURI = getExportMaskWithLeastVolumes(descriptor, equivalentExportsForVolume);
log.info("Alternatives: {} - mask with least volumes: {}", collectionString(equivalentExportsForVolume), selectedURI);
// Update the replacement mapping for this ExportMask
Map<URI, Volume> volumeMap = replaceMap.get(selectedURI);
if (volumeMap == null) {
volumeMap = new HashMap<>();
replaceMap.put(selectedURI, volumeMap);
}
volumeMap.put(volumeURI, volumes.get(volumeURI));
}
}
// Go through the replacement mapping
for (Map.Entry<URI, Map<URI, Volume>> replaceEntry : replaceMap.entrySet()) {
descriptor.placeVolumes(replaceEntry.getKey(), replaceEntry.getValue());
}
log.info("After trying to find mask alternatives:\n{}", descriptor);
}
/**
* Given a set of ExportMask URIs, which are associated with the 'descriptor', return the URI of the
* ExportMask that has the least number of volumes.
*
* @param descriptor [IN/OUT] - Context used for backend placement
* @param exportMaskURIs [IN] - Set of ExportMask URIs to search through
* @return URI of ExportMask that has the least number of volumes amongst those in the 'exportMaskURIs' set.
*/
public static URI getExportMaskWithLeastVolumes(ExportMaskPlacementDescriptor descriptor, Set<URI> exportMaskURIs) {
URI foundExportMaskURI = null;
if (exportMaskURIs.size() == 1) {
return exportMaskURIs.iterator().next();
}
Integer currentLeastVolumes = Integer.MAX_VALUE;
for (URI exportMaskURI : exportMaskURIs) {
ExportMask exportMask = descriptor.getExportMask(exportMaskURI);
int volumeCount = exportMask.returnTotalVolumeCount();
if (volumeCount < currentLeastVolumes) {
currentLeastVolumes = volumeCount;
foundExportMaskURI = exportMaskURI;
}
}
return foundExportMaskURI;
}
}