com.emc.storageos.driver.dellsc.helpers.DellSCProvisioning.java Source code

Java tutorial

Introduction

Here is the source code for com.emc.storageos.driver.dellsc.helpers.DellSCProvisioning.java

Source

/*
 * Copyright 2016 Dell Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.emc.storageos.driver.dellsc.helpers;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.mutable.MutableBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.emc.storageos.driver.dellsc.DellSCDriverException;
import com.emc.storageos.driver.dellsc.DellSCDriverTask;
import com.emc.storageos.driver.dellsc.DellSCUtil;
import com.emc.storageos.driver.dellsc.scapi.SizeUtil;
import com.emc.storageos.driver.dellsc.scapi.StorageCenterAPI;
import com.emc.storageos.driver.dellsc.scapi.StorageCenterAPIException;
import com.emc.storageos.driver.dellsc.scapi.objects.ScControllerPort;
import com.emc.storageos.driver.dellsc.scapi.objects.ScMapping;
import com.emc.storageos.driver.dellsc.scapi.objects.ScMappingProfile;
import com.emc.storageos.driver.dellsc.scapi.objects.ScPhysicalServer;
import com.emc.storageos.driver.dellsc.scapi.objects.ScServer;
import com.emc.storageos.driver.dellsc.scapi.objects.ScServerHba;
import com.emc.storageos.driver.dellsc.scapi.objects.ScServerOperatingSystem;
import com.emc.storageos.driver.dellsc.scapi.objects.ScVolume;
import com.emc.storageos.driver.dellsc.scapi.objects.ScVolumeConfiguration;
import com.emc.storageos.storagedriver.DriverTask;
import com.emc.storageos.storagedriver.DriverTask.TaskStatus;
import com.emc.storageos.storagedriver.HostExportInfo;
import com.emc.storageos.storagedriver.model.Initiator;
import com.emc.storageos.storagedriver.model.Initiator.HostOsType;
import com.emc.storageos.storagedriver.model.Initiator.Protocol;
import com.emc.storageos.storagedriver.model.Initiator.Type;
import com.emc.storageos.storagedriver.model.StorageObject.AccessStatus;
import com.emc.storageos.storagedriver.model.StoragePort;
import com.emc.storageos.storagedriver.model.StorageVolume;
import com.emc.storageos.storagedriver.storagecapabilities.ExportPathsServiceOption;
import com.emc.storageos.storagedriver.storagecapabilities.StorageCapabilities;

/**
 * Helper class for driver provisioning operations.
 */
public class DellSCProvisioning {

    private static final Logger LOG = LoggerFactory.getLogger(DellSCProvisioning.class);

    private static final String SERVER_CREATE_FAIL_MSG = "Unable to find or create server on the array.";

    private DellSCConnectionManager connectionManager;
    private DellSCUtil util;

    /**
     * Initialize the instance.
     */
    public DellSCProvisioning() {
        this.connectionManager = DellSCConnectionManager.getInstance();
        this.util = DellSCUtil.getInstance();
    }

    /**
     * Create storage volumes with a given set of capabilities.
     * Before completion of the request, set all required data for provisioned
     * volumes in "volumes" parameter.
     *
     * @param volumes Input/output argument for volumes.
     * @param storageCapabilities Input argument for capabilities. Defines
     *            storage capabilities of volumes to create.
     * @return The volume creation task.
     */
    public DriverTask createVolumes(List<StorageVolume> volumes, StorageCapabilities storageCapabilities) {
        DriverTask task = new DellSCDriverTask("createVolume");

        StringBuilder errBuffer = new StringBuilder();
        int volumesCreated = 0;
        for (StorageVolume volume : volumes) {
            LOG.debug("Creating volume {} on system {}", volume.getDisplayName(), volume.getStorageSystemId());
            String ssn = volume.getStorageSystemId();
            try {
                StorageCenterAPI api = connectionManager.getConnection(ssn);

                ScVolume scVol = api.createVolume(ssn, volume.getDisplayName(), volume.getStoragePoolId(),
                        SizeUtil.byteToMeg(volume.getRequestedCapacity()), volume.getConsistencyGroup());

                volume.setProvisionedCapacity(SizeUtil.sizeStrToBytes(scVol.configuredSize));
                volume.setAllocatedCapacity(0L); // New volumes don't allocate any space
                volume.setWwn(scVol.deviceId);
                volume.setNativeId(scVol.instanceId);
                volume.setDeviceLabel(scVol.name);
                volume.setAccessStatus(AccessStatus.READ_WRITE);

                volumesCreated++;
                LOG.info("Created volume '{}'", scVol.name);
            } catch (StorageCenterAPIException | DellSCDriverException dex) {
                String error = String.format("Error creating volume %s: %s", volume.getDisplayName(), dex);
                LOG.error(error);
                errBuffer.append(String.format("%s%n", error));
            }
        }

        task.setMessage(errBuffer.toString());

        if (volumesCreated == volumes.size()) {
            task.setStatus(TaskStatus.READY);
        } else if (volumesCreated == 0) {
            task.setStatus(TaskStatus.FAILED);
        } else {
            task.setStatus(TaskStatus.PARTIALLY_FAILED);
        }

        return task;
    }

    /**
     * Expand volume to a new size.
     * Before completion of the request, set all required data for expanded
     * volume in "volume" parameter.
     *
     * @param storageVolume Volume to expand. Type: Input/Output argument.
     * @param newCapacity Requested capacity. Type: input argument.
     * @return The volume expansion task.
     */
    public DriverTask expandVolume(StorageVolume storageVolume, long newCapacity) {
        DriverTask task = new DellSCDriverTask("expandVolume");
        try {
            StorageCenterAPI api = connectionManager.getConnection(storageVolume.getStorageSystemId());
            ScVolume scVol = api.expandVolume(storageVolume.getNativeId(), SizeUtil.byteToMeg(newCapacity));
            storageVolume.setProvisionedCapacity(SizeUtil.sizeStrToBytes(scVol.configuredSize));

            task.setStatus(TaskStatus.READY);
            LOG.info("Expanded volume '{}'", scVol.name);
        } catch (DellSCDriverException | StorageCenterAPIException dex) {
            String error = String.format("Error expanding volume %s: %s", storageVolume.getDisplayName(), dex);
            LOG.error(error);
            task.setMessage(error);
            task.setStatus(TaskStatus.FAILED);
        }

        return task;
    }

    /**
     * Delete volume.
     *
     * @param volume The volume to delete.
     * @return The volume deletion task.
     */
    public DriverTask deleteVolume(StorageVolume volume) {
        DellSCDriverTask task = new DellSCDriverTask("deleteVolume");

        try {
            StorageCenterAPI api = connectionManager.getConnection(volume.getStorageSystemId());
            api.deleteVolume(volume.getNativeId());

            task.setStatus(TaskStatus.READY);
            LOG.info("Deleted volume '{}'", volume.getNativeId());
        } catch (DellSCDriverException | StorageCenterAPIException dex) {
            String error = String.format("Error deleting volume %s", volume.getNativeId(), dex);
            LOG.error(error);
            task.setFailed(error);
        }

        return task;
    }

    /**
     * Export volumes to initiators through a given set of ports. If ports are
     * not provided, use port requirements from ExportPathsServiceOption
     * storage capability.
     *
     * @param initiators The initiators to export to.
     * @param volumes The volumes to export.
     * @param volumeToHLUMap Map of volume nativeID to requested HLU. HLU
     *            value of -1 means that HLU is not defined and will
     *            be assigned by array.
     * @param recommendedPorts List of storage ports recommended for the export.
     *            Optional.
     * @param availablePorts List of ports available for the export.
     * @param capabilities The storage capabilities.
     * @param usedRecommendedPorts True if driver used recommended and only
     *            recommended ports for the export, false
     *            otherwise.
     * @param selectedPorts Ports selected for the export (if recommended ports
     *            have not been used).
     * @return The export task.
     * @throws DellSCDriverException
     */
    public DriverTask exportVolumesToInitiators(List<Initiator> initiators, List<StorageVolume> volumes,
            Map<String, String> volumeToHLUMap, List<StoragePort> recommendedPorts,
            List<StoragePort> availablePorts, StorageCapabilities capabilities, MutableBoolean usedRecommendedPorts,
            List<StoragePort> selectedPorts) {
        LOG.info("Exporting volumes to inititators");
        DellSCDriverTask task = new DellSCDriverTask("exportVolumes");

        ScServer server = null;
        List<ScServerHba> preferredHbas = new ArrayList<>();
        StringBuilder errBuffer = new StringBuilder();
        int volumesMapped = 0;
        Set<StoragePort> usedPorts = new HashSet<>();
        String preferredController = null;

        // Cache of controller port instance IDs to StoragePort objects
        Map<String, StoragePort> discoveredPorts = new HashMap<>();

        // See if a max port count has been specified
        int maxPaths = -1;
        List<ExportPathsServiceOption> pathOptions = capabilities.getCommonCapabilitis().getExportPathParams();
        for (ExportPathsServiceOption pathOption : pathOptions) {
            // List but appears to only ever have one option?
            maxPaths = pathOption.getMaxPath();
        }

        // Get the recommended server ports to use
        List<String> recommendedServerPorts = new ArrayList<>();
        for (StoragePort port : recommendedPorts) {
            recommendedServerPorts.add(port.getNativeId());
        }

        for (StorageVolume volume : volumes) {
            String ssn = volume.getStorageSystemId();
            try {
                StorageCenterAPI api = connectionManager.getConnection(ssn);

                // Find our actual volume
                ScVolume scVol = null;

                int dotCount = StringUtils.countMatches(volume.getNativeId(), ".");
                if (dotCount == 2) {
                    // Not actually a volume
                    scVol = api.createReplayView(volume.getNativeId(),
                            String.format("View of %s", volume.getNativeId()));
                } else {
                    // Normal volume instance ID
                    scVol = api.getVolume(volume.getNativeId());
                }
                if (scVol == null) {
                    throw new DellSCDriverException(
                            String.format("Unable to find volume %s", volume.getNativeId()));
                }

                // Look up the server if needed
                if (server == null) {
                    server = createOrFindScServer(api, ssn, initiators, preferredHbas);
                }

                if (server == null) {
                    // Unable to find or create the server, can't continue
                    throw new DellSCDriverException(SERVER_CREATE_FAIL_MSG);
                }

                // See if we have a preferred controller
                if (preferredController == null && scVol.active) {
                    // At least first volume is active somewhere, so we need to try to
                    // use that controller for all mappings
                    ScVolumeConfiguration volConfig = api.getVolumeConfig(scVol.instanceId);
                    if (volConfig != null) {
                        preferredController = volConfig.controller.instanceId;
                    }
                }

                // Next try to get a preferred controller based on what's requested
                if (preferredController == null && !recommendedPorts.isEmpty()) {
                    try {
                        ScControllerPort scPort = api.getControllerPort(recommendedPorts.get(0).getNativeId());
                        preferredController = scPort.controller.instanceId;
                    } catch (Exception e) {
                        LOG.warn("Failed to get recommended port controller.", e);
                    }
                }

                int preferredLun = -1;
                if (volumeToHLUMap.containsKey(volume.getNativeId())) {
                    String hlu = volumeToHLUMap.get(volume.getNativeId());
                    try {
                        preferredLun = Integer.parseInt(hlu);
                    } catch (NumberFormatException e) {
                        LOG.warn("Unable to parse preferred LUN {}", hlu);
                    }
                }

                ScMappingProfile profile;

                // See if the volume is already mapped
                ScMappingProfile[] mappingProfiles = api.getServerVolumeMapping(scVol.instanceId,
                        server.instanceId);
                if (mappingProfiles.length > 0) {
                    // This one is already mapped
                    profile = mappingProfiles[0];
                } else {
                    profile = api.createVolumeMappingProfile(scVol.instanceId, server.instanceId, preferredLun,
                            new String[0], maxPaths, preferredController);
                }

                ScMapping[] maps = api.getMappingProfileMaps(profile.instanceId);
                for (ScMapping map : maps) {
                    volumeToHLUMap.put(volume.getNativeId(), String.valueOf(map.lun));

                    StoragePort port;
                    if (discoveredPorts.containsKey(map.controllerPort.instanceId)) {
                        port = discoveredPorts.get(map.controllerPort.instanceId);
                    } else {
                        ScControllerPort scPort = api.getControllerPort(map.controllerPort.instanceId);
                        port = util.getStoragePortForControllerPort(api, scPort);
                        discoveredPorts.put(map.controllerPort.instanceId, port);
                    }

                    usedPorts.add(port);
                }

                volumesMapped++;
                LOG.info("Volume '{}' exported to server '{}'", scVol.name, server.name);
            } catch (StorageCenterAPIException | DellSCDriverException dex) {
                String error = String.format("Error mapping volume %s: %s", volume.getDisplayName(), dex);
                LOG.error(error);
                errBuffer.append(String.format("%s%n", error));

                if (SERVER_CREATE_FAIL_MSG.equals(dex.getMessage())) {
                    // Game over
                    break;
                }
            }
        }

        // See if we were able to use all of the recommended ports
        // TODO: Expand this to do more accurate checking
        usedRecommendedPorts.setValue(recommendedPorts.size() == usedPorts.size());
        if (!usedRecommendedPorts.isTrue()) {
            selectedPorts.addAll(usedPorts);
        }

        task.setMessage(errBuffer.toString());

        if (volumesMapped == volumes.size()) {
            task.setStatus(TaskStatus.READY);
        } else if (volumesMapped == 0) {
            task.setStatus(TaskStatus.FAILED);
        } else {
            task.setStatus(TaskStatus.PARTIALLY_FAILED);
        }

        return task;
    }

    /**
     * Finds an existing server definition.
     *
     * @param ssn The Storage Center to check.
     * @param api The API connection.
     * @param initiators The list of initiators.
     * @return The server object or null.
     */
    private ScServer findScServer(StorageCenterAPI api, String ssn, List<Initiator> initiators) {
        return createOrFindScServer(api, ssn, initiators, new ArrayList<>(0), false);
    }

    /**
     * Finds an existing server definition or creates a new one.
     *
     * @param ssn The Storage Center to check.
     * @param api The API connection.
     * @param initiators The list of initiators.
     * @param matchedHbas The ScServerHbas that matched the provided initiators.
     * @return The server object or null.
     */
    private ScServer createOrFindScServer(StorageCenterAPI api, String ssn, List<Initiator> initiators,
            List<ScServerHba> matchedHbas) {
        return createOrFindScServer(api, ssn, initiators, matchedHbas, true);
    }

    /**
     * Finds an existing server definition or creates a new one.
     *
     * @param ssn The Storage Center to check.
     * @param api The API connection.
     * @param initiators The list of initiators.
     * @param matchedHbas The ScServerHbas that matched the provided initiators.
     * @param createIfNotFound Whether to create the server if it's not found.
     * @return The server object or null.
     */
    private ScServer createOrFindScServer(StorageCenterAPI api, String ssn, List<Initiator> initiators,
            List<ScServerHba> matchedHbas, boolean createIfNotFound) {
        ScServerOperatingSystem os = null;

        Map<String, ScServer> serverLookup = new HashMap<>();
        for (Initiator init : initiators) {

            if (os == null) {
                os = findOsType(api, ssn, init.getHostOsType());
            }

            if (os == null) {
                LOG.warn("Unable to find OS type for initiator {}, skipping...", init.getPort());
                continue;
            }

            String iqnOrWwn = init.getPort();
            if (init.getProtocol().equals(Protocol.FC)) {
                // Make sure it's in the format we expect
                iqnOrWwn = iqnOrWwn.replace(":", "").toUpperCase();
            }

            // Try our cache first
            ScServer individualServer = serverLookup.get(init.getHostName());
            if (individualServer == null) {
                individualServer = api.findServer(ssn, iqnOrWwn);
                if (individualServer != null) {
                    serverLookup.put(init.getHostName(), individualServer);
                }
            }

            // See if we need to create it
            if (individualServer == null && createIfNotFound) {
                try {
                    individualServer = api.createServer(ssn, init.getHostName(),
                            init.getProtocol().equals(Protocol.iSCSI), os.instanceId);
                } catch (StorageCenterAPIException e) {
                    // Well that's rather unfortunate
                    LOG.warn(String.format("Error creating server: %s", e));
                    continue;
                }

                // Need to add this initiator to existing server definition
                ScServerHba hba = api.addHbaToServer(individualServer.instanceId, iqnOrWwn,
                        init.getProtocol().equals(Protocol.iSCSI));
                if (hba != null && !matchedHbas.contains(hba)) {
                    matchedHbas.add(hba);
                }
            }

            if (individualServer != null) {
                serverLookup.put(init.getHostName(), individualServer);
            }
        }

        if (serverLookup.size() != 1) {
            LOG.warn("Looking for server returned {} servers.", serverLookup.size());
        }

        for (ScServer scServer : serverLookup.values()) {
            // Just return the first one
            return scServer;
        }

        return null;
    }

    /**
     * Find an appropriate server operating system type.
     *
     * @param hostOsType The provided OS type.
     * @return The SC OS type.
     */
    private ScServerOperatingSystem findOsType(StorageCenterAPI api, String ssn, HostOsType hostOsType) {
        String product;
        String version;
        switch (hostOsType) {
        case Windows:
            product = "Windows";
            version = "2012 MPIO";
            break;
        case HPUX:
            product = "HP UX";
            version = "11i v3";
            break;
        case Linux:
            // Behavior is pretty much the same between them, so
            // we'll just default to Red Hat
            product = "Red Hat Linux";
            version = "6.x";
            break;
        case Esx:
            product = "VMWare";
            version = "5.1";
            break;
        case AIX:
        case AIXVIO:
            product = "AIX";
            version = "7.1 MPIO";
            break;
        case SUNVCS:
        case No_OS:
        case Other:
        default:
            product = "Other";
            version = "Multipath";
            break;
        }

        ScServerOperatingSystem[] osTypes = api.getServerOperatingSystems(ssn, product);

        // First try for an exact match
        for (ScServerOperatingSystem os : osTypes) {
            if (os.version.contains(version)) {
                return os;
            }
        }

        // Just get the first one
        for (ScServerOperatingSystem os : osTypes) {
            return os;
        }

        // Fall back to other if we can
        if (hostOsType != HostOsType.Other) {
            LOG.warn("Unable to find OS type, falling back to Other type.");
            return findOsType(api, ssn, HostOsType.Other);
        }

        return null;
    }

    /**
     * Remove volume exports to initiators.
     *
     * @param initiators The initiators to remove from.
     * @param volumes The volumes to remove.
     * @return The unexport task.
     */
    public DriverTask unexportVolumesFromInitiators(List<Initiator> initiators, List<StorageVolume> volumes) {
        LOG.info("Unexporting volumes from initiators");
        DriverTask task = new DellSCDriverTask("unexportVolumes");

        ScServer server = null;
        StringBuilder errBuffer = new StringBuilder();
        int volumesUnmapped = 0;

        for (StorageVolume volume : volumes) {
            String ssn = volume.getStorageSystemId();
            boolean isSnapshot = StringUtils.countMatches(volume.getNativeId(), ".") == 2;
            try {
                StorageCenterAPI api = connectionManager.getConnection(ssn);

                // Find our actual volume
                ScVolume scVol = null;

                if (isSnapshot) {
                    scVol = api.findReplayView(volume.getNativeId());

                    // For snapshot views we can just delete the view
                    if (scVol != null) {
                        api.deleteVolume(scVol.instanceId);
                        volumesUnmapped++;
                        continue;
                    }
                } else {
                    scVol = api.getVolume(volume.getNativeId());
                }

                if (scVol == null) {
                    throw new DellSCDriverException(
                            String.format("Unable to find volume %s", volume.getNativeId()));
                }

                // Look up the server if needed
                if (server == null) {
                    server = findScServer(api, ssn, initiators);
                }

                if (server == null) {
                    // Unable to find the server, can't continue
                    throw new DellSCDriverException(SERVER_CREATE_FAIL_MSG);
                }

                ScMappingProfile[] mappingProfiles = api.findMappingProfiles(server.instanceId, scVol.instanceId);
                for (ScMappingProfile mappingProfile : mappingProfiles) {
                    api.deleteMappingProfile(mappingProfile.instanceId);
                }
                volumesUnmapped++;
                LOG.info("Volume '{}' unexported from server '{}'", scVol.name, server.name);
            } catch (StorageCenterAPIException | DellSCDriverException dex) {
                String error = String.format("Error unmapping volume %s: %s", volume.getDisplayName(), dex);
                LOG.error(error);
                errBuffer.append(String.format("%s%n", error));

                if (SERVER_CREATE_FAIL_MSG.equals(dex.getMessage())) {
                    // Game over
                    break;
                }
            }
        }

        task.setMessage(errBuffer.toString());

        if (volumesUnmapped == volumes.size()) {
            task.setStatus(TaskStatus.READY);
        } else if (volumesUnmapped == 0) {
            task.setStatus(TaskStatus.FAILED);
        } else {
            task.setStatus(TaskStatus.PARTIALLY_FAILED);
        }

        return task;
    }

    /**
     * Gets the mapping information for a volume to initiators.
     *
     * @param volumeId The volume instance ID.
     * @param systemId The storage system ID.
     * @return The mapping details. Map of HostName:HostExportInfo
     */
    public Map<String, HostExportInfo> getVolumeExportInfo(String volumeId, String systemId) {
        Map<String, HostExportInfo> result = new HashMap<>();

        Map<String, ScServer> serverCache = new HashMap<>();
        Map<String, Initiator> serverPortCache = new HashMap<>();
        Map<String, StoragePort> portCache = new HashMap<>();

        try {
            StorageCenterAPI api = connectionManager.getConnection(systemId);
            ScVolume scVol = api.getVolume(volumeId);
            if (scVol == null) {
                throw new DellSCDriverException(String.format("Volume %s could not be found.", volumeId));
            }

            ScMapping[] maps = api.getVolumeMaps(scVol.instanceId);
            for (ScMapping map : maps) {
                populateVolumeExportInfo(api, volumeId, map, result, serverCache, serverPortCache, portCache);
            }
        } catch (StorageCenterAPIException | DellSCDriverException dex) {
            String message = String.format("Error getting export info for volume %s: %s", volumeId, dex);
            LOG.warn(message);
        }

        return result;
    }

    /**
     * Fills in export information for a specific volume mapping.
     *
     * @param volumeId The volume instance ID.
     * @param map The mapping.
     * @param result The export info map.
     * @param serverCache The server cache.
     * @param serverPortCache The server HBA cache.
     * @param portCache The controller port cache.
     * @throws StorageCenterAPIException
     */
    private void populateVolumeExportInfo(StorageCenterAPI api, String volumeId, ScMapping map,
            Map<String, HostExportInfo> result, Map<String, ScServer> serverCache,
            Map<String, Initiator> serverPortCache, Map<String, StoragePort> portCache)
            throws StorageCenterAPIException {
        ScServer server;
        Initiator initiator;
        StoragePort port;

        // Get our server info
        if (serverCache.containsKey(map.server.instanceId)) {
            server = serverCache.get(map.server.instanceId);
        } else {
            server = api.getServerDefinition(map.server.instanceId);
            serverCache.put(server.instanceId, server);
        }

        // Find the server HBA
        if (serverPortCache.containsKey(map.serverHba.instanceId)) {
            initiator = serverPortCache.get(map.serverHba.instanceId);
        } else {
            ScServerHba hba = api.getServerHba(map.serverHba.instanceId);
            initiator = getInitiatorInfo(api, server, hba);
            serverPortCache.put(hba.instanceId, initiator);
        }

        // Get the controller port info
        if (portCache.containsKey(map.controllerPort.instanceId)) {
            port = portCache.get(map.controllerPort.instanceId);
        } else {
            ScControllerPort scPort = api.getControllerPort(map.controllerPort.instanceId);
            port = util.getStoragePortForControllerPort(api, scPort);
            portCache.put(scPort.instanceId, port);
        }

        String hostName = initiator.getHostName();
        if (initiator.getInitiatorType() == Type.Cluster) {
            hostName = initiator.getClusterName();
        }

        HostExportInfo exportInfo;
        if (result.containsKey(hostName)) {
            exportInfo = result.get(hostName);
        } else {
            exportInfo = new HostExportInfo(hostName, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
        }

        // Now populate all the info
        if (!exportInfo.getStorageObjectNativeIds().contains(volumeId)) {
            exportInfo.getStorageObjectNativeIds().add(volumeId);
        }

        if (!exportInfo.getTargets().contains(port)) {
            exportInfo.getTargets().add(port);
        }

        if (!exportInfo.getInitiators().contains(initiator)) {
            exportInfo.getInitiators().add(initiator);
        }

        result.put(hostName, exportInfo);
    }

    /**
     * Gets Initiator details from servers and ports.
     *
     * @param api The Storage Center API connection.
     * @param server The ScServer.
     * @param hba The server HBA.
     * @return The initiator.
     */
    private Initiator getInitiatorInfo(StorageCenterAPI api, ScServer server, ScServerHba hba) {
        Initiator result = new Initiator();

        ScServer cluster = null;
        if (server.type.contains("Physical")) {
            ScPhysicalServer physicalServer = api.getPhysicalServerDefinition(server.instanceId);
            if (physicalServer != null && physicalServer.parent != null
                    && physicalServer.parent.instanceId != null) {
                cluster = api.getServerDefinition(physicalServer.parent.instanceId);
            }
        }

        result.setHostName(server.name);
        result.setInitiatorType(Type.Host);

        if (cluster != null) {
            result.setClusterName(cluster.name);
            result.setInitiatorType(Type.Cluster);
        }

        result.setNativeId(hba.instanceId);
        result.setPort(hba.instanceId);

        Protocol protocol = Protocol.iSCSI;
        if ("FibreChannel".equals(hba.portType)) {
            protocol = Protocol.FC;
        }
        result.setProtocol(protocol);

        return result;
    }
}