/* * Copyright (c) 2016 EMC Corporation * * All Rights Reserved */ package com.emc.storageos.networkcontroller.impl; import java.io.Serializable; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.ExportGroup; import com.emc.storageos.db.client.model.ExportMask; import com.emc.storageos.db.client.model.Initiator; import com.emc.storageos.db.client.model.StorageProtocol.Transport; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.StringSetMap; import com.emc.storageos.db.client.util.StringSetUtil; import com.emc.storageos.volumecontroller.impl.utils.ExportMaskUtils; import com.emc.storageos.workflow.WorkflowException; public class NetworkZoningParam implements Serializable { private static final long serialVersionUID = 678350596864970920L; /** * This is the zoning map obtained from the ExportMask. * It is a map of initiator URI string to a set of port URI strings. * Zones will be added or removed as derived from the zoning map. */ private StringSetMap zoningMap; /** * Boolean indicating the the ExportMask had additional additional volumes * to be considered. If this boolean is true, the zone references will be * removed, but zones will not be removed. */ private boolean hasExistingVolumes; /** * The virtualArray is used to determine if zoning is enabled. */ private URI virtualArray; /** * THe altVirtualArray is used in searching for initiators, ports, etc. sed by * the VPLEX in the alternate virtual array. */ private URI altVirtualArray; /** * The exportGroupId is used as part of the FCZoneReferences. */ private URI exportGroupId; /** * Alternate Export Group ids. */ private List<URI> alternateExportGroupIds; /** * The ExportGroup display string, used for logging. */ private String exportGroupDisplay; /** * Name of the ExportMask the parameters were derived from. Used for logging. */ private String maskName; /** * URI of the ExportMask the parameters were derived from. Used for logging. */ private URI maskId; /* * Export mask volumes. */ private List<URI> volumes; /** * Generates the zoning parameters from an ExportGroup/ExportMask pair. * @param exportGroup ExportGroup * @param exportMask ExportMask * @param dbClient Database Handle */ public NetworkZoningParam(ExportGroup exportGroup, ExportMask exportMask, DbClient dbClient) { String storageSystem = exportMask.getStorageDevice().toString(); setVirtualArray(exportGroup.getVirtualArray()); if (exportGroup.hasAltVirtualArray(storageSystem)) { setAltVirtualArray(URI.create(exportGroup.getAltVirtualArrays().get(storageSystem))); } setHasExistingVolumes(exportMask.hasAnyExistingVolumes()); setExportGroup(exportGroup.getId()); setExportGroupDisplay(exportGroup.forDisplay()); setMaskName(exportMask.getMaskName()); setMaskId(exportMask.getId()); if (exportMask.getVolumes() != null) { setVolumes(StringSetUtil.stringSetToUriList(exportMask.getVolumes().keySet())); } else { setVolumes(new ArrayList<URI>()); } Set<Initiator> initiators = ExportMaskUtils.getInitiatorsForExportMask(dbClient, exportMask, Transport.FC); NetworkScheduler.checkZoningMap(exportGroup, exportMask, initiators, dbClient); setZoningMap(exportMask.getZoningMap()); } /** * Converts a list of ExportMask to a list of NetworkZoningParam blocks containing * the required attributes from the ExportMask. * @param exportGroupURI -- URI of ExportGroup that is being operated on * @param exportMaskURIs -- List of URIs for ExportMasks to be converted * @param dbClient -- database handle * @return list of NetworkZoningParam * @throws WorkflowException if any mask is not active */ static public List<NetworkZoningParam> convertExportMasksToNetworkZoningParam( URI exportGroupURI, List<URI> exportMaskURIs, DbClient dbClient) { ExportGroup exportGroup = dbClient.queryObject(ExportGroup.class, exportGroupURI); List<NetworkZoningParam> zoningParams = new ArrayList<NetworkZoningParam>(); for (URI exportMaskURI : exportMaskURIs) { ExportMask exportMask = dbClient.queryObject(ExportMask.class, exportMaskURI); if (exportMask == null || exportMask.getInactive()) { throw WorkflowException.exceptions.workflowConstructionError( "ExportMask is null: " + exportMaskURI.toString()); } NetworkZoningParam zoningParam = new NetworkZoningParam(exportGroup, exportMask, dbClient); zoningParams.add(zoningParam); } return zoningParams; } /** * Generates a list of NetworkZoningParam objects from a map of export mask URI to a list of initiator URIs. * Only the initiators in the exportMaskToInitiators map are retained from the ExportMask initiators. * @param exportGroupURI * @param exportMaskToInitiators * @param dbClient * @return */ static public List<NetworkZoningParam> convertExportMaskInitiatorMapsToNetworkZoningParam( URI exportGroupURI, Map<URI, List<URI>> exportMaskToInitiators, DbClient dbClient) { ExportGroup exportGroup = dbClient.queryObject(ExportGroup.class, exportGroupURI); List<NetworkZoningParam> zoningParams = new ArrayList<NetworkZoningParam>(); for (Map.Entry<URI, List<URI>> entry : exportMaskToInitiators.entrySet()) { ExportMask exportMask = dbClient.queryObject(ExportMask.class, entry.getKey()); if (exportMask == null || exportMask.getInactive()) { throw WorkflowException.exceptions.workflowConstructionError( "ExportMask is null: " + entry.getKey().toString()); } NetworkZoningParam zoningParam = new NetworkZoningParam(exportGroup, exportMask, dbClient); // Filter out entries in the zoning map not in the initiator list. // This is done by retaining all the initiators in the exportMaskToInitiators value. Set<String> retainedInitiators = StringSetUtil.uriListToSet(entry.getValue()); zoningParam.getZoningMap().keySet().retainAll(retainedInitiators); // Add zoningParam to result zoningParams.add(zoningParam); } return zoningParams; } /** * Generate a list of NetworkZoningParam objects when removing paths in path adjustment (port rebalance). * This is not as straight forward as it might appear, because each ExportMask may also be * referenced by alternate Export Groups, and their references would also need to be removed. * * @param exportGroupURI -- The invoking EG URI * @param exportMaskURI -- The export mask being processed * @param maskRemovePaths -- paths that will be removed * @param dbClient -- database client * @return */ static public List<NetworkZoningParam> convertPathsToNetworkZoningParam( URI exportGroupURI, Map<URI, Map<URI, List<URI>>> maskRemovePaths, DbClient dbClient) { List<NetworkZoningParam> result = new ArrayList<NetworkZoningParam>(); ExportGroup exportGroup = dbClient.queryObject(ExportGroup.class, exportGroupURI); // Iterate over all the Export Masks for (Map.Entry<URI, Map<URI, List<URI>>> maskEntry : maskRemovePaths.entrySet()) { URI maskURI = maskEntry.getKey(); ExportMask mask = dbClient.queryObject(ExportMask.class, maskURI); Map<URI, List<URI>> maskPaths = maskEntry.getValue(); if (maskPaths.isEmpty()) { continue; } NetworkZoningParam zoningParam = new NetworkZoningParam(exportGroup, mask, dbClient); StringSetMap zoningMap = mask.getZoningMap(); StringSetMap convertedZoningMap = new StringSetMap(); // Get the path entries in both of the exportMask zoning map and the paths for (Map.Entry<URI, List<URI>> entry : maskPaths.entrySet()) { URI init = entry.getKey(); List<URI> pathPorts = entry.getValue(); StringSet ports = zoningMap.get(init.toString()); if (ports != null && !ports.isEmpty()) { StringSet convertedPorts = new StringSet(); for (URI pathPort : pathPorts) { if (ports.contains(pathPort.toString())) { convertedPorts.add(pathPort.toString()); } } if (!convertedPorts.isEmpty()) { convertedZoningMap.put(init.toString(), convertedPorts); } } } zoningParam.setZoningMap(convertedZoningMap); List<ExportGroup> allExportGroupsUsingMask = ExportMaskUtils.getExportGroups(dbClient, mask); for (ExportGroup altExportGroup : allExportGroupsUsingMask) { if (zoningParam.getAlternateExportGroupIds() == null) { zoningParam.setAlternateExportGroupIds(new ArrayList<URI>()); } if (!altExportGroup.getId().equals(exportGroupURI)) { zoningParam.getAlternateExportGroupIds().add(altExportGroup.getId()); } } result.add(zoningParam); } return result; } public StringSetMap getZoningMap() { return zoningMap; } public void setZoningMap(StringSetMap zoningMap) { this.zoningMap = zoningMap; } public boolean hasExistingVolumes() { return hasExistingVolumes; } public void setHasExistingVolumes(boolean hasExistingVolumes) { this.hasExistingVolumes = hasExistingVolumes; } public URI getVirtualArray() { return virtualArray; } public void setVirtualArray(URI virtualArray) { this.virtualArray = virtualArray; } public URI getAltVirtualArray() { return altVirtualArray; } public void setAltVirtualArray(URI altVirtualArray) { this.altVirtualArray = altVirtualArray; } public URI getExportGroupId() { return exportGroupId; } public void setExportGroup(URI exportGroupId) { this.exportGroupId = exportGroupId; } public String getMaskName() { return maskName; } public void setMaskName(String maskName) { this.maskName = maskName; } public URI getMaskId() { return maskId; } public void setMaskId(URI maskURI) { this.maskId = maskURI; } public String getExportGroupDisplay() { return exportGroupDisplay; } public void setExportGroupDisplay(String exportGroupDisplay) { this.exportGroupDisplay = exportGroupDisplay; } public List<URI> getVolumes() { return volumes; } public void setVolumes(List<URI> volumes) { this.volumes = volumes; } public List<URI> getAlternateExportGroupIds() { return alternateExportGroupIds; } public void setAlternateExportGroupIds(List<URI> alternateExportGroupIds) { this.alternateExportGroupIds = alternateExportGroupIds; } }