/*
* Copyright (c) 2015. EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.block;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.ExportMask;
import com.emc.storageos.util.ExportUtils;
/**
* This class has a template function that encapsulates the selection of ExportMasks based on volumes. It requires that
* a context parameter with all relevant data be passed in. Derived classes should implement the abstract methods to
* customize the selection
*/
public abstract class VmaxVolumeToExportMaskRuleApplicator {
private static final Logger log = LoggerFactory.getLogger(VmaxVolumeToExportMaskRuleApplicator.class);
private final DbClient dbClient;
protected final VmaxVolumeToExportMaskApplicatorContext context;
public VmaxVolumeToExportMaskRuleApplicator(DbClient dbClient, VmaxVolumeToExportMaskApplicatorContext context) {
this.context = context;
this.dbClient = dbClient;
}
/**
* This method will be invoked after successful 'applyRules' calls. Any post processing that needs to run after the rules have been
* applies should be done here. It is up to the implementer of this method to determine what, if any, processing needs to be done.
*
* @param initiatorsForResource [IN] - List of Initiator URIs that we are trying to export to
* @param initiatorsToVolumes [IN] - Mapping of Initiator URIs to Volumes (Map of Volume URI to its HLU value)
* @throws Exception
*/
abstract public void postApply(List<URI> initiatorsForResource, Map<URI, Map<URI, Integer>> initiatorsToVolumes) throws Exception;
/**
* The 'initiatorsToVolumes' parameter encapsulates the export for a particular compute resource. The 'run()' method determines,
* based on the VmaxVolumeToExportMaskApplicatorContext, what the compute resources are and calls this method against each. If
* applyRules is successful, it should return true and update the context with results. Successful applyRules calls will result
* in a call to postApply.
*
* If the applyRules fails, it should return false. In the case of a failure, the 'run()' method will not process any more
* compute resources. It will set context.resultSuccess to false and return. So, the client of the
* VmaxVolumeToExportMaskRuleApplicator implementation needs to check context.resultSuccess before looking at any results
* that the context may contain.
*
* @param initiatorsToVolumes [IN] - Mapping of Initiator URIs to Volumes (Map of Volume URI to its HLU value)
* @return true IFF rules were successfully applied
*/
abstract public boolean applyRules(Map<URI, Map<URI, Integer>> initiatorsToVolumes);
/**
* This is a template routine. The expectation is that the derived classes will
* fill in the abstract methods that are called within it
*/
public void run() throws Exception {
// Basic input: initiators, their associated ExportMasks, and volumes to export
Map<String, Set<URI>> initiatorToExportMaskPlacementMap = context.initiatorToExportMaskPlacementMap;
AbstractDefaultMaskingOrchestrator.InitiatorHelper initiatorHelper = context.initiatorHelper;
Map<URI, Integer> volumeMap = context.volumeMap;
// Master policy map can be used as additional contextual information
context.exportMaskToPolicy = createPolicyMap();
// Determine the compute resources based on the initiators and then group by that resource name/ID
Map<String, Set<URI>> computeResourceInitiatorsMap = AbstractDefaultMaskingOrchestrator.createResourceMaskMap(
initiatorHelper.getPortNameToInitiatorURI(), initiatorHelper.getResourceToInitiators(), initiatorToExportMaskPlacementMap);
// Foreach compute resource ...
for (Map.Entry<String, Set<URI>> resourceEntry : computeResourceInitiatorsMap.entrySet()) {
String computeResource = resourceEntry.getKey();
// Get its initiators & map them to the volumes to export ...
List<URI> initiatorsForResource = initiatorHelper.getResourceToInitiators().get(computeResource);
Map<URI, Map<URI, Integer>> initiatorsToVolumes = mapComputeResourceInitiatorsToVolumes(initiatorsForResource, volumeMap);
// Run the customized rules referencing the initiators & volumes ...
if (applyRules(initiatorsToVolumes)) {
// If the rules were successfully applies, run the post operation
postApply(initiatorsForResource, initiatorsToVolumes);
} else {
// The operation failed, mark it as failed and exit
context.resultSuccess = false;
return;
}
}
// If we got here, there were no applyRules() failures, so success!
context.resultSuccess = true;
}
/**
* Using the contextual information produce a mapping of ExportMask to its ExportMaskPolicy.
*
* @return Map of ExportMask object to ExportMaskPolicy
*/
private Map<ExportMask, ExportMaskPolicy> createPolicyMap() {
// Translate the ExportMask URI to ExportMaskPolicy cache to a mapping of ExportMask object to ExportMaskPolicy
Map<ExportMask, ExportMaskPolicy> policyMap = new HashMap<>();
Iterator<ExportMask> exportMasks = dbClient.queryIterativeObjects(ExportMask.class, context.exportMaskURIToPolicy.keySet());
while (exportMasks.hasNext()) {
ExportMask mask = exportMasks.next();
// Check for NO_VIPR. If found, avoid this mask.
if (mask.getMaskName() != null && mask.getMaskName().toUpperCase().contains(ExportUtils.NO_VIPR)) {
log.info(
String.format("ExportMask %s disqualified because the name contains %s (in upper or lower case) to exclude it",
mask.getMaskName(), ExportUtils.NO_VIPR));
continue;
}
ExportMaskPolicy policy = context.exportMaskURIToPolicy.get(mask.getId());
if (policy != null) {
policyMap.put(mask, policy);
} else {
log.error("Could not find an ExportMaskPolicy for {} ({})", mask.getMaskName(), mask.getId());
}
}
return policyMap;
}
/**
* Create a mapping of the initiators to all the volumes
*
* @param initiatorsForResource [IN] - Initiators URIs that belong to a particular resource
* @param volumeMap [IN] - Volumes that need to mapped to the initiators
* @return Map of the Initiator URIs to Volumes
*/
private Map<URI, Map<URI, Integer>> mapComputeResourceInitiatorsToVolumes(List<URI> initiatorsForResource,
Map<URI, Integer> volumeMap) {
Map<URI, Map<URI, Integer>> initiatorsToVolumes = new HashMap<>();
for (URI initiatorId : initiatorsForResource) {
Map<URI, Integer> vols = initiatorsToVolumes.get(initiatorId);
if (vols == null) {
vols = new HashMap<>();
initiatorsToVolumes.put(initiatorId, vols);
}
vols.putAll(volumeMap);
}
return initiatorsToVolumes;
}
}