/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.vnxe;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.model.ExportGroup;
import com.emc.storageos.db.client.model.ExportMask;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.util.CommonTransformerFunctions;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.volumecontroller.BlockStorageDevice;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl;
import com.emc.storageos.volumecontroller.impl.block.AbstractBasicMaskingOrchestrator;
import com.emc.storageos.volumecontroller.impl.block.MaskingWorkflowEntryPoints;
import com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportOrchestrationTask;
import com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportTaskCompleter;
import com.emc.storageos.volumecontroller.impl.utils.ExportMaskUtils;
import com.emc.storageos.workflow.Workflow;
import com.google.common.base.Joiner;
import com.google.common.collect.Collections2;
public class VNXeMaskingOrchestrator extends AbstractBasicMaskingOrchestrator {
private static final Logger _log = LoggerFactory.getLogger(VNXeMaskingOrchestrator.class);
private static final AtomicReference<BlockStorageDevice> VNXE_BLOCK_DEVICE = new AtomicReference<BlockStorageDevice>();
public static final String VNXE_DEVICE = "vnxeDevice";
public static final String DEFAULT_LABEL = "Default";
@Override
public BlockStorageDevice getDevice() {
BlockStorageDevice device = VNXE_BLOCK_DEVICE.get();
synchronized (VNXE_BLOCK_DEVICE) {
if (device == null) {
device = (BlockStorageDevice) ControllerServiceImpl.getBean(VNXE_DEVICE);
VNXE_BLOCK_DEVICE.compareAndSet(null, device);
}
}
return device;
}
/**
* Create storage level masking components to support the requested
* ExportGroup object. ExportMask will be created for each host.
*
*
* @param storageURI
* - URI referencing underlying storage array
* @param exportGroupURI
* - URI referencing Bourne-level masking, ExportGroup
* @param initiatorURIs
* - List of Initiator URIs
* @param volumeMap
* - Map of Volume URIs to requested Integer URI
* @param token
* - Identifier for operation
* @throws Exception
*/
@Override
public void exportGroupCreate(URI storageURI, URI exportGroupURI, List<URI> initiatorURIs,
Map<URI, Integer> volumeMap, String token) throws Exception {
ExportOrchestrationTask taskCompleter = null;
try {
BlockStorageDevice device = getDevice();
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI);
taskCompleter = new ExportOrchestrationTask(exportGroupURI, token);
if (initiatorURIs != null && !initiatorURIs.isEmpty()) {
_log.info("export_create: initiator list non-empty");
// Set up workflow steps.
Workflow workflow = _workflowService.getNewWorkflow(
MaskingWorkflowEntryPoints.getInstance(), "exportGroupCreate", true, token);
// Create two steps, one for Zoning, one for the ExportGroup actions.
// This step is for zoning. It is not specific to a single
// NetworkSystem, as it will look at all the initiators and targets and compute
// the zones required (which might be on multiple NetworkSystems.)
boolean createdSteps = determineExportGroupCreateSteps(workflow, null, device, storage, exportGroup,
initiatorURIs, volumeMap, token);
String zoningStep = generateZoningCreateWorkflow(workflow, EXPORT_GROUP_MASKING_TASK, exportGroup,
null, volumeMap);
if (createdSteps) {
// Execute the plan and allow the WorkflowExecutor to fire the
// taskCompleter.
String successMessage = String.format(
"ExportGroup successfully applied for StorageArray %s", storage.getLabel());
workflow.executePlan(taskCompleter, successMessage);
}
} else {
_log.info("export_create: initiator list");
taskCompleter.ready(_dbClient);
}
} catch (Exception ex) {
_log.error("ExportGroup Orchestration failed.", ex);
// TODO add service code here
if (taskCompleter != null) {
ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(ex.getMessage(), ex);
taskCompleter.error(_dbClient, serviceError);
}
}
}
@Override
public void exportGroupDelete(URI storageURI,
URI exportGroupURI,
String token) throws Exception {
try {
_log.info(String.format("exportGroupDelete start - Array: %s ExportMask: %s",
storageURI.toString(), exportGroupURI.toString()));
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class,
exportGroupURI);
StorageSystem storage = _dbClient.queryObject(StorageSystem.class,
storageURI);
TaskCompleter taskCompleter = new ExportOrchestrationTask(exportGroupURI,
token);
if (exportGroup == null || exportGroup.getInactive() || ExportMaskUtils.getExportMasks(_dbClient, exportGroup, storageURI).isEmpty()) {
taskCompleter.ready(_dbClient);
return;
}
/**
* If no export mask is found, nothing to be done. Task will be marked
* complete by the last real export mask delete completion.
*/
List<ExportMask> exportMasks = ExportMaskUtils.getExportMasks(_dbClient,
exportGroup, storageURI);
Workflow workflow = _workflowService.getNewWorkflow(
MaskingWorkflowEntryPoints.getInstance(),
"exportGroupDelete", true, token);
String deleteStep = null;
for (ExportMask exportMask : exportMasks) {
refreshExportMask(storage, getDevice(), exportMask);
deleteStep = generateExportMaskDeleteWorkflow(workflow, deleteStep,
storage, exportGroup, exportMask, null, null, null);
}
generateZoningDeleteWorkflow(workflow, deleteStep,
exportGroup, exportMasks);
String successMessage = String.format(
"Export was successfully removed from StorageArray %s",
storage.getLabel());
workflow.executePlan(taskCompleter, successMessage);
_log.info(String.format("exportGroupDelete end - Array: %s ExportMask: %s",
storageURI.toString(), exportGroupURI.toString()));
} catch (Exception e) {
throw DeviceControllerException.exceptions.exportGroupDeleteFailed(e);
}
}
@Override
public void exportGroupAddInitiators(URI storageURI, URI exportGroupURI,
List<URI> initiatorURIs,
String token) throws Exception {
TaskCompleter taskCompleter = null;
try {
_log.info(String.format("exportAddInitiator start - Array: %s ExportMask: " +
"%s Initiator: %s",
storageURI.toString(), exportGroupURI.toString(),
Joiner.on(',').join(initiatorURIs)));
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class,
exportGroupURI);
StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI);
List<ExportMask> exportMasks = ExportMaskUtils.getExportMasks(_dbClient,
exportGroup, storageURI);
Map<String, List<URI>> computeResourceToInitiators = mapInitiatorsToComputeResource(
exportGroup, initiatorURIs);
_log.info("initiators : {}", Joiner.on(",").join(computeResourceToInitiators.entrySet()));
taskCompleter = new ExportOrchestrationTask(exportGroupURI, token);
Map<URI, Integer> volumes = selectExportMaskVolumes(exportGroup, storageURI);
_log.info("Volumes : {}", Joiner.on(",").join(volumes.keySet()));
if (!CollectionUtils.isEmpty(exportMasks)) {
// Refresh all export masks
for (ExportMask exportMask : exportMasks) {
refreshExportMask(storage, getDevice(), exportMask);
}
// find the export mask which has the same Host name as the initiator
// Add the initiator to that export mask
// Set up workflow steps.
_log.info("Creating AddInitiators workFlow");
Workflow workflow = _workflowService.getNewWorkflow(
MaskingWorkflowEntryPoints.getInstance(),
"exportGroupAddInitiators", true, token, taskCompleter);
// irrespective of cluster name, host will be always present
Map<String, URI> hostToEMaskGroup = ExportMaskUtils.mapHostToExportMask(
_dbClient, exportGroup, storage.getId());
_log.info("hostsToExportMask : {}", Joiner.on(",").join(hostToEMaskGroup.entrySet()));
// if export masks are found for the Host, then add initiators to the export mask
Map<URI, List<URI>> masksToInitiators = new HashMap<URI, List<URI>>();
String addIniStep = null;
for (String computeKey : computeResourceToInitiators.keySet()) {
URI exportMaskUri = hostToEMaskGroup.get(computeKey);
if (null != exportMaskUri) {
_log.info("Processing export mask {}", exportMaskUri);
ExportMask exportMask = _dbClient.queryObject(ExportMask.class,
exportMaskUri);
if (exportMask.getStorageDevice().equals(storageURI)) {
_log.info("Processing export mask {} with expected storage {}", exportMaskUri, storageURI);
// AddInitiatorWorkFlow
masksToInitiators.put(exportMaskUri,
computeResourceToInitiators.get(computeKey));
// all masks will be always created by system = true, hence port allocation will happen
addIniStep = generateExportMaskAddInitiatorsWorkflow(workflow, null,
storage, exportGroup, exportMask, initiatorURIs, null,
token);
computeResourceToInitiators.remove(computeKey);
}
if (!masksToInitiators.isEmpty()) {
generateZoningAddInitiatorsWorkflow(
workflow, addIniStep, exportGroup, masksToInitiators);
}
}
}
_log.info("Left out initiators : {}", Joiner.on(",").join(computeResourceToInitiators.entrySet()));
// left out initiator's Host which doesn't have any export mask.
Map<URI, Map<URI, Integer>> zoneNewMasksToVolumeMap = new HashMap<URI, Map<URI, Integer>>();
if (!computeResourceToInitiators.isEmpty()) {
for (Map.Entry<String, List<URI>> resourceEntry : computeResourceToInitiators
.entrySet()) {
String computeKey = resourceEntry.getKey();
List<URI> computeInitiatorURIs = resourceEntry.getValue();
_log.info(String.format("New export masks for %s", computeKey));
GenExportMaskCreateWorkflowResult result = generateExportMaskCreateWorkflow(
workflow, EXPORT_GROUP_ZONING_TASK, storage, exportGroup,
computeInitiatorURIs, volumes, token);
zoneNewMasksToVolumeMap.put(result.getMaskURI(), volumes);
}
if (!zoneNewMasksToVolumeMap.isEmpty()) {
List<URI> exportMaskList = new ArrayList<URI>();
exportMaskList.addAll(zoneNewMasksToVolumeMap.keySet());
Map<URI, Integer> overallVolumeMap = new HashMap<URI, Integer>();
for (Map<URI, Integer> oneVolumeMap : zoneNewMasksToVolumeMap
.values()) {
overallVolumeMap.putAll(oneVolumeMap);
}
generateZoningCreateWorkflow(workflow, null, exportGroup,
exportMaskList, overallVolumeMap);
}
}
String successMessage = String.format(
"Initiators successfully added to export StorageArray %s",
storage.getLabel());
workflow.executePlan(taskCompleter, successMessage);
} else {
_log.info("export_initiator_add: first initiator, creating a new export");
// No existing export masks available inexport Group
Workflow workflow = _workflowService.getNewWorkflow(
MaskingWorkflowEntryPoints.getInstance(), "exportGroupCreate",
true, token, taskCompleter);
List<URI> exportMasksToZoneCreate = new ArrayList<URI>();
Map<URI, Integer> volumesToZoneCreate = new HashMap<URI, Integer>();
for (Map.Entry<String, List<URI>> resourceEntry : computeResourceToInitiators
.entrySet()) {
String computeKey = resourceEntry.getKey();
List<URI> computeInitiatorURIs = resourceEntry.getValue();
_log.info(String.format("New export masks for %s", computeKey));
GenExportMaskCreateWorkflowResult result = generateExportMaskCreateWorkflow(
workflow, EXPORT_GROUP_ZONING_TASK, storage, exportGroup,
computeInitiatorURIs, volumes, token);
exportMasksToZoneCreate.add(result.getMaskURI());
volumesToZoneCreate.putAll(volumes);
}
if (!exportMasksToZoneCreate.isEmpty()) {
generateZoningCreateWorkflow(workflow, null, exportGroup,
exportMasksToZoneCreate, volumesToZoneCreate);
}
String successMessage = String.format(
"Initiators successfully added to export StorageArray %s",
storage.getLabel());
workflow.executePlan(taskCompleter, successMessage);
}
_log.info(String.format("exportAddInitiator end - Array: %s ExportMask: %s " +
"Initiator: %s",
storageURI.toString(), exportGroupURI.toString(),
Joiner.on(',').join(initiatorURIs)));
} catch (Exception e) {
if (taskCompleter != null) {
ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(e.getMessage(), e);
taskCompleter.error(_dbClient, serviceError);
} else {
throw DeviceControllerException.exceptions.exportGroupAddInitiatorsFailed(e);
}
}
}
@Override
public void exportGroupRemoveInitiators(URI storageURI,
URI exportGroupURI,
List<URI> initiatorURIs,
String token) throws Exception {
ExportTaskCompleter taskCompleter = null;
try {
List<Initiator> initiators = _dbClient.queryObject(Initiator.class,
initiatorURIs);
_log.info(String.format("exportRemoveInitiator start - Array: %s " +
"ExportMask: %s Initiator: %s",
storageURI.toString(), exportGroupURI.toString(),
Joiner.on(',').join(initiatorURIs)));
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class,
exportGroupURI);
StorageSystem storage = _dbClient.queryObject(StorageSystem.class,
storageURI);
taskCompleter = new ExportOrchestrationTask(exportGroupURI, token);
// Set up workflow steps.
Workflow workflow = _workflowService.getNewWorkflow(
MaskingWorkflowEntryPoints.getInstance(),
"exportGroupRemoveInitiators", true, token);
/**
* export mask must exist since both volume & initiator exist
*/
Map<ExportMask, List<Initiator>> exportMasksMap = getInitiatorExportMasks(initiators, _dbClient, exportGroup, storageURI);
Map<URI, List<URI>> maskToInitiatorsMap = new HashMap<URI, List<URI>>();
for (Entry<ExportMask, List<Initiator>> entry : exportMasksMap.entrySet()) {
ExportMask mask = entry.getKey();
List<Initiator> inits = entry.getValue();
List<URI> initURIList = new ArrayList<URI>();
for (Initiator init : inits) {
initURIList.add(init.getId());
}
maskToInitiatorsMap.put(mask.getId(), initURIList);
}
String deleteStep = null;
for (ExportMask exportMask : exportMasksMap.keySet()) {
refreshExportMask(storage, getDevice(), exportMask);
List<Initiator> inits = exportMasksMap.get(exportMask);
if (exportMask.getInitiators().size() == inits.size() &&
exportMask.getVolumes() != null) {
_log.info(String.format("deleting the exportMask: %s",
exportMask.getId().toString()));
// Initiator list (initiatorURIs) need to be provided when deleting export mask as a result of removing last initiators
deleteStep = generateExportMaskDeleteWorkflow(workflow, deleteStep, storage,
exportGroup, exportMask, null, initiatorURIs, null);
} else {
Collection<URI> volumeURIs = (Collections2.transform(exportMask.getVolumes().keySet(),
CommonTransformerFunctions.FCTN_STRING_TO_URI));
generateExportMaskRemoveInitiatorsWorkflow(workflow, deleteStep,
storage, exportGroup, exportMask, new ArrayList<URI>(volumeURIs), initiatorURIs, true);
}
_log.info(String.format("exportRemoveInitiator end - Array: %s ExportMask: %s",
storageURI.toString(), exportGroupURI.toString()));
}
generateZoningRemoveInitiatorsWorkflow(workflow, deleteStep,
exportGroup, maskToInitiatorsMap);
String successMessage = String.format(
"Initiators successfully removed from export StorageArray %s",
storage.getLabel());
workflow.executePlan(taskCompleter, successMessage);
} catch (Exception e) {
if (taskCompleter != null) {
ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(e.getMessage(), e);
taskCompleter.error(_dbClient, serviceError);
} else {
throw DeviceControllerException.exceptions.exportGroupRemoveInitiatorsFailed(e);
}
}
}
@Override
public void exportGroupAddVolumes(URI storageURI, URI exportGroupURI,
Map<URI, Integer> volumeMap,
String token) throws Exception {
ExportTaskCompleter taskCompleter = null;
try {
_log.info(
String.format("exportAddVolume start - Array: %s ExportMask: %s Volume: %s",
storageURI.toString(), exportGroupURI.toString(),
Joiner.on(',').join(volumeMap.entrySet())));
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI);
taskCompleter = new ExportOrchestrationTask(exportGroupURI, token);
List<ExportMask> exportMasks = ExportMaskUtils.getExportMasks(_dbClient, exportGroup, storageURI);
if (!CollectionUtils.isEmpty(exportMasks)) {
// refresh all export masks
for (ExportMask exportMask : exportMasks) {
refreshExportMask(storage, getDevice(), exportMask);
}
// Set up workflow steps.
Workflow workflow = _workflowService.getNewWorkflow(
MaskingWorkflowEntryPoints.getInstance(),
"exportGroupAddVolumes - Added volumes to existing mask", true,
token);
List<URI> volumeURIs = new ArrayList<URI>();
volumeURIs.addAll(volumeMap.keySet());
Collection<URI> initiatorURIs = Collections2.transform(exportGroup.getInitiators(),
CommonTransformerFunctions.FCTN_STRING_TO_URI);
findAndUpdateFreeHLUsForClusterExport(storage, exportGroup, new ArrayList<URI>(initiatorURIs), volumeMap);
String zoningStep = generateZoningAddVolumesWorkflow(workflow, null,
exportGroup, exportMasks, volumeURIs);
String exportStep = null;
for (ExportMask exportMask : exportMasks) {
if (exportStep == null) {
exportStep = generateExportMaskAddVolumesWorkflow(workflow, zoningStep, storage,
exportGroup, exportMask, volumeMap, null);
} else {
exportStep = generateExportMaskAddVolumesWorkflow(workflow, exportStep, storage,
exportGroup, exportMask, volumeMap, null);
}
}
String successMessage = String.format(
"Volumes successfully added to export on StorageArray %s",
storage.getLabel());
workflow.executePlan(taskCompleter, successMessage);
} else {
// This is the case when exportGroup exists, but no volume is added before.
if (exportGroup.getInitiators() != null && !exportGroup.getInitiators().isEmpty()) {
_log.info("export_volume_add: adding volume, creating a new export");
List<URI> initiatorURIs = new ArrayList<URI>();
for (String initiatorId : exportGroup.getInitiators()) {
initiatorURIs.add(URI.create(initiatorId));
}
exportGroupCreate(storageURI, exportGroupURI, initiatorURIs, volumeMap, token);
} else {
_log.info("export_volume_add: adding volume, no initiators yet");
taskCompleter.ready(_dbClient);
}
}
_log.info(String.format("exportAddVolume end - Array: %s ExportMask: %s Volume: %s",
storageURI.toString(), exportGroupURI.toString(),
volumeMap.toString()));
} catch (Exception e) {
if (taskCompleter != null) {
ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(e.getMessage(), e);
taskCompleter.error(_dbClient, serviceError);
} else {
throw DeviceControllerException.exceptions.exportGroupAddVolumesFailed(e);
}
}
}
@Override
public void exportGroupRemoveVolumes(URI storageURI, URI exportGroupURI,
List<URI> volumes,
String token) throws Exception {
ExportTaskCompleter taskCompleter = null;
try {
_log.info(
String.format("exportRemoveVolume start - Array: %s ExportMask: %s " +
"Volume: %s",
storageURI.toString(), exportGroupURI.toString(),
Joiner.on(',').join(volumes)));
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class,
exportGroupURI);
StorageSystem storage = _dbClient.queryObject(StorageSystem.class,
storageURI);
taskCompleter = new ExportOrchestrationTask(exportGroupURI, token);
List<ExportMask> exportMasks = ExportMaskUtils.getExportMasks(_dbClient,
exportGroup, storageURI);
if (exportMasks != null && !exportMasks.isEmpty()) {
Workflow workflow = _workflowService.getNewWorkflow(
MaskingWorkflowEntryPoints.getInstance(),
"exportGroupRemoveVolumes", true, token);
List<ExportMask> deleteMasks = new ArrayList<ExportMask>();
List<ExportMask> updateMasks = new ArrayList<ExportMask>();
for (ExportMask mask : exportMasks) {
refreshExportMask(storage, getDevice(), mask);
// Determine if we're deleting the last volume.
Set<String> remainingVolumes = new HashSet<String>();
if (mask.getVolumes() != null) {
remainingVolumes.addAll(mask.getVolumes().keySet());
}
for (URI volume : volumes) {
remainingVolumes.remove(volume.toString());
}
// If so, delete the ExportMask.
if (remainingVolumes.isEmpty()) {
deleteMasks.add(mask);
} else {
updateMasks.add(mask);
}
}
if (!deleteMasks.isEmpty()) {
String deleteStep = null;
for (ExportMask exportMask : deleteMasks) {
deleteStep = generateExportMaskDeleteWorkflow(workflow, deleteStep, storage,
exportGroup, exportMask, null, null, null);
}
generateZoningDeleteWorkflow(workflow, deleteStep,
exportGroup, deleteMasks);
}
if (!updateMasks.isEmpty()) {
String unexportStep = null;
for (ExportMask exportMask : updateMasks) {
unexportStep = generateExportMaskRemoveVolumesWorkflow(workflow, unexportStep,
storage, exportGroup, exportMask, volumes, null, null);
}
generateZoningRemoveVolumesWorkflow(workflow,
null, exportGroup, updateMasks, volumes);
}
String successMessage = String.format(
"Volumes successfully unexported from StorageArray %s",
storage.getLabel());
workflow.executePlan(taskCompleter, successMessage);
} else {
_log.info("export_volume_remove: no export (initiator should be empty)");
exportGroup.removeVolumes(volumes);
_dbClient.updateObject(exportGroup);
taskCompleter.ready(_dbClient);
}
_log.info(String.format("exportRemoveVolume end - Array: %s ExportMask: %s " +
"Volume: %s",
storageURI.toString(), exportGroupURI.toString(),
Joiner.on(',').join(volumes)));
} catch (Exception e) {
if (taskCompleter != null) {
ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(e.getMessage(), e);
taskCompleter.error(_dbClient, serviceError);
} else {
throw DeviceControllerException.exceptions.exportRemoveVolumes(e);
}
}
}
@Override
public void findAndUpdateFreeHLUsForClusterExport(StorageSystem storage, ExportGroup exportGroup, List<URI> initiatorURIs,
Map<URI, Integer> volumeMap) {
findUpdateFreeHLUsForClusterExport(storage, exportGroup, initiatorURIs, volumeMap);
}
/**
* Routine contains logic to create an export mask on the array
*
* @param workflow
* - Workflow object to create steps against
* @param previousStep
* - [optional] Identifier of workflow step to wait for
* @param device
* - BlockStorageDevice implementation
* @param storage
* - StorageSystem object representing the underlying array
* @param exportGroup
* - ExportGroup object representing Bourne-level masking
* @param initiatorURIs
* - List of Initiator URIs
* @param volumeMap
* - Map of Volume URIs to requested Integer HLUs
* @param token
* - Identifier for the operation
* @throws Exception
*/
private boolean determineExportGroupCreateSteps(Workflow workflow, String previousStep,
BlockStorageDevice device, StorageSystem storage, ExportGroup exportGroup,
List<URI> initiatorURIs, Map<URI, Integer> volumeMap, String token) throws Exception {
Map<String, URI> portNameToInitiatorURI = new HashMap<String, URI>();
List<URI> volumeURIs = new ArrayList<URI>();
volumeURIs.addAll(volumeMap.keySet());
List<URI> hostURIs = new ArrayList<URI>();
List<String> portNames = new ArrayList<String>();
// Populate the port WWN/IQNs (portNames) and the
// mapping of the WWN/IQNs to Initiator URIs
processInitiators(exportGroup, initiatorURIs, portNames, portNameToInitiatorURI, hostURIs);
// We always want to have the full list of initiators for the hosts involved in
// this export. This will allow the export operation to always find any
// existing exports for a given host.
queryHostInitiatorsAndAddToList(portNames, portNameToInitiatorURI,
initiatorURIs, hostURIs);
findAndUpdateFreeHLUsForClusterExport(storage, exportGroup, initiatorURIs, volumeMap);
// Bogus URI for those initiators without a host object, helps maintain a good map.
// We want to put bunch up the non-host initiators together.
URI fillerHostURI = URIUtil.createId(Host.class); // could just be NullColumnValueGetter.getNullURI()
if (!initiatorURIs.isEmpty()) {
Map<URI, List<URI>> hostInitiatorMap = new HashMap<URI, List<URI>>();
for (URI newExportMaskInitiator : initiatorURIs) {
Initiator initiator = _dbClient.queryObject(Initiator.class, newExportMaskInitiator);
// Not all initiators have hosts, be sure to handle either case.
URI hostURI = initiator.getHost();
if (hostURI == null) {
hostURI = fillerHostURI;
}
List<URI> initiatorSet = hostInitiatorMap.get(hostURI);
if (initiatorSet == null) {
initiatorSet = new ArrayList<URI>();
hostInitiatorMap.put(hostURI, initiatorSet);
}
initiatorSet.add(initiator.getId());
_log.info(String.format("host = %s, "
+ "initiators to add: %d, ",
hostURI,
hostInitiatorMap.get(hostURI).size()));
}
if (!hostInitiatorMap.isEmpty()) {
String exportStep = null;
for (URI hostID : hostInitiatorMap.keySet()) {
_log.info(String.format("new export masks %s",
Joiner.on(",").join(hostInitiatorMap.get(hostID))));
if (exportStep == null) {
GenExportMaskCreateWorkflowResult result = generateExportMaskCreateWorkflow(workflow, previousStep, storage,
exportGroup,
hostInitiatorMap.get(hostID), volumeMap, token);
exportStep = result.getStepId();
} else {
GenExportMaskCreateWorkflowResult result = generateExportMaskCreateWorkflow(workflow, exportStep, storage,
exportGroup,
hostInitiatorMap.get(hostID), volumeMap, token);
exportStep = result.getStepId();
}
}
}
}
return true;
}
private Map<ExportMask, List<Initiator>> getInitiatorExportMasks(
List<Initiator> initiators, DbClient dbClient, ExportGroup exportGroup, URI ssysURI) {
Map<ExportMask, List<Initiator>> exportMasksMap = new HashMap<ExportMask, List<Initiator>>();
List<ExportMask> exportMasks = ExportMaskUtils.getExportMasks(dbClient, exportGroup, ssysURI);
for (ExportMask exportMask : exportMasks) {
List<Initiator> maskInitiators = new ArrayList<Initiator>();
for (Initiator initiator : initiators) {
_log.info("initiator to be removed: {}", initiator.getId().toString());
if (exportMask != null &&
!exportMask.getInactive() &&
exportMask.hasInitiator(initiator.getId().toString())) {
_log.info("initiator is in the mask: {}", exportMask.getId().toString());
maskInitiators.add(initiator);
}
}
if (!maskInitiators.isEmpty()) {
exportMasksMap.put(exportMask, maskInitiators);
}
}
return exportMasksMap;
}
}