/* * Copyright (c) 2016 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.xiv; import java.net.URI; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.BlockMirror; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.Cluster; import com.emc.storageos.db.client.model.DataObject; 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.ScopedLabel; import com.emc.storageos.db.client.model.ScopedLabelSet; import com.emc.storageos.db.client.model.StorageProvider; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringMap; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.networkcontroller.impl.NetworkDeviceController; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.util.ExportUtils; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.VolumeURIHLU; import com.emc.storageos.volumecontroller.impl.utils.ExportMaskUtils; import com.emc.storageos.xiv.api.XIVRestClient; import com.emc.storageos.xiv.api.XIVRestClient.HOST_STATUS; import com.emc.storageos.xiv.api.XIVRestClientFactory; import com.emc.storageos.xiv.api.XIVRestException; import com.google.common.base.Joiner; /** * Helper class to support all REST operations performed on IBM XIV - Hyper Scale manager. * * Current operations supported: * Find if a Host is part of cluster on XIV * Create Export Mask * */ public class XIVRestOperationsHelper { private static Logger _log = LoggerFactory.getLogger(XIVRestOperationsHelper.class); private DbClient _dbClient; private XIVRestClientFactory _restClientFactory; private static final int MAXIMUM_LUN = 511; private static final String INVALID_LUN_ERROR_MSG = "Logical unit number provided (%d) is larger than allowed (%d)."; public void setDbClient(DbClient dbClient) { _dbClient = dbClient; } public void setXivRestClientFactory(XIVRestClientFactory factory) { _restClientFactory = factory; } /** * Gets REST Client instance for a StorageSystem * * @param storage * StorageSystem instance * @return XIVRESTExportOperations instance */ private XIVRestClient getRestClient(StorageSystem storage) { XIVRestClient restClient = null; StorageProvider provider = _dbClient.queryObject(StorageProvider.class, storage.getActiveProviderURI()); String providerUser = provider.getSecondaryUsername(); String providerPassword = provider.getSecondaryPassword(); String providerURL = provider.getSecondaryURL(); if (StringUtils.isNotEmpty(providerURL) && StringUtils.isNotEmpty(providerPassword) && StringUtils.isNotEmpty(providerUser)) { restClient = (XIVRestClient) _restClientFactory.getRESTClient(URI.create(providerURL), providerUser, providerPassword); } return restClient; } /** * Validates if the Host is part of a Cluster on XIV system. Uses initiators to find out the Hosts * * @param storage XIV Storage System instance * @param exportMaskURI ExportMask instance * @param initiatorList Host Initiators * @param exportType Host/Cluster export type of the operation * @return True if the host is part of Cluster else false. */ public boolean isClusteredHost(StorageSystem storage, List<Initiator> initiators, String exportType) { boolean isClusteredHost = false; if (null != initiators && !initiators.isEmpty()) { Set<String> hostNames = new HashSet<String>(); XIVRestClient restExportOpr = getRestClient(storage); if (null == restExportOpr) { return isClusteredHost; } // From Initiators find all the Hosts on Array for (Initiator initiator : initiators) { try { final String hostName = restExportOpr.getHostNameFromPort(storage.getSmisProviderIP(), Initiator.normalizePort(initiator.getInitiatorPort())); if (null != hostName) { hostNames.add(hostName); } } catch (Exception e) { _log.error("Invalid host : {} or Port : {} for HSM from Storage System : {} ", storage.getSmisProviderIP(), initiator.getInitiatorPort(), storage.getLabel()); final String errorMessage = "Unable to contact Hyper Scale Manager to execute operation. Either Host or Port could be invalid."; throw XIVRestException.exceptions.errorInHSMHostConfiguration(errorMessage); } } // Check if Host is part of Cluster if (hostNames.isEmpty()) { if (ExportGroup.ExportGroupType.Cluster.name().equals(exportType)) { isClusteredHost = true; } } else { Set<Boolean> result = new HashSet<Boolean>(); for (String hostName : hostNames) { result.add(isClusteredHostOnArray(storage, hostName)); } isClusteredHost = (result.size() == 1 ? result.iterator().next() : false); } } return isClusteredHost; } /** * Validates if the Host is part of a Cluster on XIV system. Uses initiators to find out the Hosts * * @param storage * XIV Storage System instance * @param exportMaskURI * ExportMask instance * @param initiatorList * Host Initiators * @return True if the host is part of Cluster else false. */ public boolean isClusteredHost(StorageSystem storage, List<Initiator> initiators) { return isClusteredHost(storage, initiators, null); } /** * Validates if the given Host name is identified as Clustered host with respect to XIV * * @param storage * XIV storage system * @param hostName * Host name to ve validated * @return true if the host is part of Cluster. Else false. */ private boolean isClusteredHostOnArray(StorageSystem storage, String hostName) { boolean isClusteredHost = false; XIVRestClient restExportOpr = getRestClient(storage); if (null != restExportOpr && null != hostName) { HOST_STATUS hostStatus = null; try { hostStatus = restExportOpr.getHostStatus(storage.getSmisProviderIP(), hostName); } catch (Exception e) { _log.error("Unable to validate host {} information on array : {} ", hostName, storage.getLabel(), e); } if (null != hostStatus) { if (HOST_STATUS.HOST_NOT_PRESENT.equals(hostStatus)) { _log.info("Host {} not present on Array {}. Creating a new instance!", hostName, storage.getLabel()); isClusteredHost = true; } else if (HOST_STATUS.CLUSTER_HOST.equals(hostStatus)) { _log.info("Identified Host {} as a Clustered Host on Array {}.", hostName, storage.getLabel()); isClusteredHost = true; } else if (HOST_STATUS.STANDALONE_HOST.equals(hostStatus)) { _log.info("Host {} identified as a Standalone host on Array {}. Using SMIS for provisioning!", hostName, storage.getLabel()); isClusteredHost = false; } } } return isClusteredHost; } /** * Creates Export mask for a Cluster. Creates Cluster, Host and Inititators on XIV. Exports the volume to the * Cluster. * * @param storage * XIV Storage System * @param exportMaskURI * Export Mask URI * @param volumeURIHLUs * Volume URIs to be exported * @param targetURIList * Target ports (not used for XIV) * @param initiatorList * Initiator ports * @param taskCompleter * Task Completer instance */ public void createRESTExportMask(StorageSystem storage, URI exportMaskURI, VolumeURIHLU[] volumeURIHLUs, List<URI> targetURIList, List<Initiator> initiatorList, TaskCompleter taskCompleter) { try { ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI); XIVRestClient restExportOpr = getRestClient(storage); final String storageIP = storage.getSmisProviderIP(); String exportName = null; String clusterName = null; URI clusterURI = null; Set<String> hosts = new HashSet<String>(); for (Initiator initiator : initiatorList) { final Host host = _dbClient.queryObject(Host.class, initiator.getHost()); exportName = host.getLabel(); hosts.add(exportName); clusterURI = host.getCluster(); } final String exportType = ExportMaskUtils.getExportType(_dbClient, exportMask); if (ExportGroup.ExportGroupType.Cluster.name().equals(exportType) && null != clusterURI) { Cluster cluster = _dbClient.queryObject(Cluster.class, clusterURI); clusterName = cluster.getLabel(); exportName = clusterName; // Create Cluster if not exist restExportOpr.createCluster(storageIP, clusterName); } // Create Host if not exist for (String hostName : hosts) { restExportOpr.createHost(storageIP, clusterName, hostName); } List<Initiator> userAddedInitiator = new ArrayList<Initiator>(); List<BlockObject> userAddedVolumes = new ArrayList<BlockObject>(); for (Initiator initiator : initiatorList) { final Host host = _dbClient.queryObject(Host.class, initiator.getHost()); // Add Initiators to Host. if (!restExportOpr.createHostPort(storageIP, host.getLabel(), Initiator.normalizePort(initiator.getInitiatorPort()), initiator.getProtocol().toLowerCase())) { userAddedInitiator.add(initiator); } } // Export volume to Cluster if (volumeURIHLUs != null && volumeURIHLUs.length > 0) { for (VolumeURIHLU volumeURIHLU : volumeURIHLUs) { final BlockObject blockObject = getBlockObject(volumeURIHLU.getVolumeURI()); final String volumeHLU = volumeURIHLU.getHLU(); if (volumeHLU != null && !volumeHLU.equalsIgnoreCase(ExportGroup.LUN_UNASSIGNED_STR)) { int hluDec = Integer.parseInt(volumeHLU, 16); if (hluDec > MAXIMUM_LUN) { String errMsg = String.format(INVALID_LUN_ERROR_MSG, hluDec, MAXIMUM_LUN); _log.error(errMsg); throw new Exception(errMsg); } else { if (!restExportOpr.exportVolume(storageIP, exportType, exportName, blockObject.getLabel(), String.valueOf(hluDec), isSnapshot(volumeURIHLU.getVolumeURI()))) { userAddedVolumes.add(blockObject); } } } } } // Update Masking information exportMask.setCreatedBySystem(true); exportMask.addToUserCreatedInitiators(userAddedInitiator); exportMask.addToUserCreatedVolumes(userAddedVolumes); exportMask.setMaskName(exportName); exportMask.setNativeId(exportName); exportMask.setLabel(exportName); _dbClient.updateObject(exportMask); taskCompleter.ready(_dbClient); } catch (Exception e) { _log.error("Unexpected error: createRESTExportMask failed.", e); XIVRestException error = XIVRestException.exceptions.methodFailed("createExportMask", e); taskCompleter.error(_dbClient, error); } } /** * This method will take a URI and return alternateName for the BlockObject object to which the * URI applies. * * @param uri * - URI * @return Returns a nativeId String value * @throws XIVRestException.exceptions.notAVolumeOrBlocksnapshotUri * if URI is not a Volume/BlockSnapshot URI */ private BlockObject getBlockObject(URI uri) throws Exception { BlockObject object; if (URIUtil.isType(uri, Volume.class)) { object = _dbClient.queryObject(Volume.class, uri); } else if (URIUtil.isType(uri, BlockSnapshot.class)) { object = _dbClient.queryObject(BlockSnapshot.class, uri); } else if (URIUtil.isType(uri, BlockMirror.class)) { object = _dbClient.queryObject(BlockMirror.class, uri); } else { throw XIVRestException.exceptions.notAVolumeOrBlocksnapshotUri(uri); } return object; } /** * Refresh the export mask with the user added configuration * * @param storage * XIV sotrage system * @param mask * Export Mask instance * @param _networkDeviceController * Network configuration instance */ public void refreshRESTExportMask(StorageSystem storage, ExportMask mask, NetworkDeviceController _networkDeviceController) { try { final String storageIP = storage.getSmisProviderIP(); final String name = mask.getNativeId(); XIVRestClient restExportOpr = getRestClient(storage); StringBuilder builder = new StringBuilder(); Set<String> discoveredPorts = new HashSet<String>(); Set<URI> hostURIs = new HashSet<URI>(); Set<Initiator> exportMaskInits = ExportMaskUtils.getInitiatorsForExportMask(_dbClient, mask, null); Iterator<Initiator> exportMaskInitsItr = exportMaskInits.iterator(); while (exportMaskInitsItr.hasNext()) { hostURIs.add(exportMaskInitsItr.next().getHost()); } // Check the initiators and update the lists as necessary List<Host> hostList = _dbClient.queryObject(Host.class, hostURIs); for (Host host : hostList) { discoveredPorts.addAll(restExportOpr.getHostPorts(storageIP, host.getLabel())); } boolean addInitiators = false; List<String> initiatorsToAdd = new ArrayList<String>(); List<Initiator> initiatorIdsToAdd = new ArrayList<>(); for (String port : discoveredPorts) { String normalizedPort = Initiator.normalizePort(port); if (!mask.hasExistingInitiator(normalizedPort) && !mask.hasUserInitiator(normalizedPort)) { initiatorsToAdd.add(normalizedPort); Initiator existingInitiator = ExportUtils.getInitiator(Initiator.toPortNetworkId(port), _dbClient); if (existingInitiator != null) { initiatorIdsToAdd.add(existingInitiator); } addInitiators = true; } } boolean removeInitiators = false; List<String> initiatorsToRemove = new ArrayList<String>(); List<URI> initiatorIdsToRemove = new ArrayList<>(); if (mask.getExistingInitiators() != null && !mask.getExistingInitiators().isEmpty()) { initiatorsToRemove.addAll(mask.getExistingInitiators()); initiatorsToRemove.removeAll(discoveredPorts); } removeInitiators = !initiatorsToRemove.isEmpty(); // Get Volumes mapped to a Host on Array Map<String, Integer> discoveredVolumes = new HashMap<String, Integer>(); final String exportType = ExportMaskUtils.getExportType(_dbClient, mask); if (ExportGroup.ExportGroupType.Cluster.name().equals(exportType)) { discoveredVolumes.putAll(restExportOpr.getVolumesMappedToHost(storageIP, mask.getLabel(), null)); } else { for (Host host : hostList) { discoveredVolumes.putAll(restExportOpr.getVolumesMappedToHost(storageIP, null, host.getLabel())); } } // Check the volumes and update the lists as necessary Map<String, Integer> volumesToAdd = ExportMaskUtils.diffAndFindNewVolumes(mask, discoveredVolumes); boolean addVolumes = !volumesToAdd.isEmpty(); boolean removeVolumes = false; List<String> volumesToRemove = new ArrayList<String>(); if (mask.getExistingVolumes() != null && !mask.getExistingVolumes().isEmpty()) { volumesToRemove.addAll(mask.getExistingVolumes().keySet()); volumesToRemove.removeAll(discoveredVolumes.keySet()); removeVolumes = !volumesToRemove.isEmpty(); } builder.append(String.format( "XM refresh: %s initiators; add:{%s} remove:{%s}%n", name, Joiner.on(',').join(initiatorsToAdd), Joiner.on(',').join(initiatorsToRemove))); builder.append(String.format( "XM refresh: %s volumes; add:{%s} remove:{%s}%n", name, Joiner.on(',').join(volumesToAdd.keySet()), Joiner.on(',').join(volumesToRemove))); if (addInitiators || removeInitiators || addVolumes || removeVolumes) { builder.append("XM refresh: There are changes to mask, " + "updating it...\n"); mask.removeFromExistingInitiators(initiatorsToRemove); if (initiatorIdsToRemove != null && !initiatorIdsToRemove.isEmpty()) { mask.removeInitiators(_dbClient.queryObject(Initiator.class, initiatorIdsToRemove)); } List<Initiator> userAddedInitiators = ExportMaskUtils.findIfInitiatorsAreUserAddedInAnotherMask(mask, initiatorIdsToAdd, _dbClient); mask.addToUserCreatedInitiators(userAddedInitiators); mask.addToExistingInitiatorsIfAbsent(initiatorsToAdd); mask.addInitiators(initiatorIdsToAdd); mask.removeFromExistingVolumes(volumesToRemove); mask.setExistingVolumes(new StringMap()); mask.addToExistingVolumesIfAbsent(volumesToAdd); ExportMaskUtils.sanitizeExportMaskContainers(_dbClient, mask); _dbClient.updateObject(mask); } else { builder.append("XM refresh: There are no changes to the mask\n"); } _networkDeviceController.refreshZoningMap(mask, initiatorsToRemove, Collections.EMPTY_LIST, (addInitiators || removeInitiators), true); _log.info(builder.toString()); } catch (Exception e) { String msg = "Error when attempting to query LUN masking information: " + e.getMessage(); _log.error(MessageFormat.format("Encountered an error when attempting to refresh existing exports: {0}", msg), e); throw XIVRestException.exceptions.refreshExistingMaskFailure(msg); } } /** * Deletes the Export Mask and its attributes * * @param storage * XIV storage system * @param exportMaskURI * Export mask URI * @param volumeURIList * Volume URI as list * @param targetURIList * target port URI as list [ not used for xiv] * @param initiatorList * Initiator port URI as list * @param taskCompleter * task completer instance */ public void deleteRESTExportMask(StorageSystem storage, URI exportMaskURI, List<URI> volumeURIList, List<URI> targetURIList, List<Initiator> initiatorList, TaskCompleter taskCompleter) { try { ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI); final String storageIP = storage.getSmisProviderIP(); final String exportType = ExportMaskUtils.getExportType(_dbClient, exportMask); final String name = exportMask.getNativeId(); final StringSet emInitiatorURIs = exportMask.getInitiators(); final StringMap emVolumeURIs = exportMask.getVolumes(); XIVRestClient restExportOpr = getRestClient(storage); Set<URI> hostURIs = new HashSet<URI>(); // Un export Volumes if (null != emVolumeURIs) { Iterator<Entry<String, String>> emVolumeURIItr = emVolumeURIs.entrySet().iterator(); while (emVolumeURIItr.hasNext()) { URI volUri = URI.create(emVolumeURIItr.next().getKey()); if (URIUtil.isType(volUri, Volume.class)) { Volume volume = _dbClient.queryObject(Volume.class, volUri); restExportOpr.unExportVolume(storageIP, exportType, name, volume.getLabel()); } } } // Delete initiators if (null != emInitiatorURIs) { for (String initiatorURI : emInitiatorURIs) { Initiator initiator = _dbClient.queryObject(Initiator.class, URI.create(initiatorURI)); Host host = _dbClient.queryObject(Host.class, initiator.getHost()); hostURIs.add(host.getId()); String normalizedPort = Initiator.normalizePort(initiator.getInitiatorPort()); restExportOpr.deleteHostPort(storageIP, host.getLabel(), normalizedPort, initiator.getProtocol().toLowerCase(), false); } } // Delete Host if there are no associated Initiators/Volume to it. for (URI hostURI : hostURIs) { Host host = _dbClient.queryObject(Host.class, hostURI); boolean hostDeleted = restExportOpr.deleteHost(storageIP, host.getLabel(), false); // Perform post-mask-delete cleanup steps if (hostDeleted && emVolumeURIs.size() > 0) { unsetTag(host, storage.getSerialNumber()); } } // Delete Cluster if there is no associated hosts to it. if (ExportGroup.ExportGroupType.Cluster.name().equals(exportType)) { restExportOpr.deleteCluster(storageIP, name); } ExportUtils.cleanupAssociatedMaskResources(_dbClient, exportMask); exportMask.setMaskName(NullColumnValueGetter.getNullURI().toString()); exportMask.setLabel(NullColumnValueGetter.getNullURI().toString()); exportMask.setNativeId(NullColumnValueGetter.getNullURI().toString()); exportMask.setResource(NullColumnValueGetter.getNullURI().toString()); _dbClient.updateObject(exportMask); taskCompleter.ready(_dbClient); } catch (Exception e) { _log.error("Unexpected error: deleteExportMask failed.", e); XIVRestException error = XIVRestException.exceptions.methodFailed("createExportMask", e); taskCompleter.error(_dbClient, error); } } private void setTag(DataObject object, String scope, String label) { if (label == null) { // shouldn't happen label = ""; } ScopedLabel newScopedLabel = new ScopedLabel(scope, label); ScopedLabelSet tagSet = object.getTag(); if (tagSet == null) { tagSet = new ScopedLabelSet(); tagSet.add(newScopedLabel); object.setTag(tagSet); } else if (tagSet.contains(newScopedLabel)) { return; } else { removeLabel(tagSet, scope); tagSet.add(newScopedLabel); } _dbClient.persistObject(object); } private void unsetTag(DataObject object, String scope) { ScopedLabelSet tagSet = object.getTag(); if (tagSet == null) { return; } removeLabel(tagSet, scope); _dbClient.updateObject(object); } private void removeLabel(ScopedLabelSet tagSet, String scope) { ScopedLabel oldScopedLabel = null; Iterator<ScopedLabel> itr = tagSet.iterator(); while (itr.hasNext()) { ScopedLabel scopedLabel = itr.next(); if (scope.equals(scopedLabel.getScope())) { oldScopedLabel = scopedLabel; break; } } if (oldScopedLabel != null) { tagSet.remove(oldScopedLabel); } } /** * Find Export mask * * @param storage * Storage System instance * @param initiatorNames * Initiator names * @param mustHaveAllPorts * Must have all the ports boolean * @return Initiator to Export mask map. */ public Map<String, Set<URI>> findRESTExportMasks(StorageSystem storage, List<String> initiatorNames, boolean mustHaveAllPorts) { long startTime = System.currentTimeMillis(); Map<String, Set<URI>> matchingMasks = new HashMap<String, Set<URI>>(); try { final String storageIP = storage.getSmisProviderIP(); XIVRestClient restExportOpr = getRestClient(storage); StringBuilder builder = new StringBuilder(); for (String initiatorName : initiatorNames) { final String hostName = restExportOpr.getHostPortContainer(storageIP, initiatorName); Set<String> exportMaskNames = new HashSet<String>(); if (null != hostName) { exportMaskNames.add(hostName); final String clusterNames = restExportOpr.getHostContainer(storageIP, hostName); if (null != clusterNames) { exportMaskNames.add(clusterNames); } } // Find the existing masks for (String exportMaskName : exportMaskNames) { URIQueryResultList uriQueryList = new URIQueryResultList(); _dbClient.queryByConstraint(AlternateIdConstraint.Factory.getExportMaskByNameConstraint(exportMaskName), uriQueryList); ExportMask exportMask = null; while (uriQueryList.iterator().hasNext()) { URI uri = uriQueryList.iterator().next(); exportMask = _dbClient.queryObject(ExportMask.class, uri); if (exportMask != null && !exportMask.getInactive() && exportMask.getStorageDevice().equals(storage.getId())) { ExportMaskUtils.sanitizeExportMaskContainers(_dbClient, exportMask); _dbClient.updateAndReindexObject(exportMask); Set<URI> maskURIs = matchingMasks.get(initiatorName); if (maskURIs == null) { maskURIs = new HashSet<URI>(); matchingMasks.put(initiatorName, maskURIs); } maskURIs.add(exportMask.getId()); break; } } // update hosts Initiator initiator = ExportUtils.getInitiator(Initiator.toPortNetworkId(initiatorName), _dbClient); if (null != initiator && null != initiator.getHost()) { Host hostIns = _dbClient.queryObject(Host.class, initiator.getHost()); String label = hostIns.getLabel(); if (label.equals(exportMaskName)) { unsetTag(hostIns, storage.getSerialNumber()); } else { setTag(hostIns, storage.getSerialNumber(), exportMaskName); } } } } _log.info(builder.toString()); } catch (Exception e) { String msg = "Error when attempting to query LUN masking information: " + e.getMessage(); _log.error(MessageFormat.format("Encountered an SMIS error when attempting to query existing exports: {0}", msg), e); throw XIVRestException.exceptions.queryExistingMasksFailure(msg, e); } finally { long totalTime = System.currentTimeMillis() - startTime; _log.info(String.format("findExportMasks took %f seconds", (double) totalTime / (double) 1000)); } return matchingMasks; } /** * Adds volumes to an export mask * * @param storage * Storage system instance * @param exportMaskURI * Export mask URI * @param volumeURIHLUs * Volume to be added URI * @param initiatorList * initiators to be valiated against for this operation * @param taskCompleter * task completer instance */ public void addVolumesUsingREST(StorageSystem storage, URI exportMaskURI, VolumeURIHLU[] volumeURIHLUs, List<Initiator> initiatorList, TaskCompleter taskCompleter) { _log.info("{} addVolume START...", storage.getLabel()); try { // Export volume to Cluster if (volumeURIHLUs != null && volumeURIHLUs.length > 0) { ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI); final String storageIP = storage.getSmisProviderIP(); XIVRestClient restExportOpr = getRestClient(storage); // Find HOST from Export Mask URI hostName = null; Set<Initiator> exportMaskInits = ExportMaskUtils.getInitiatorsForExportMask(_dbClient, exportMask, null); Iterator<Initiator> exportMaskInitsItr = exportMaskInits.iterator(); if (exportMaskInitsItr.hasNext()) { hostName = exportMaskInitsItr.next().getHost(); } final Host host = _dbClient.queryObject(Host.class, hostName); // Validate if it is a cluster String exportName = host.getLabel(); String clusterName = null; final String exportType = ExportMaskUtils.getExportType(_dbClient, exportMask); if (ExportGroup.ExportGroupType.Cluster.name().equals(exportType)) { Cluster cluster = _dbClient.queryObject(Cluster.class, host.getCluster()); clusterName = cluster.getLabel(); exportName = clusterName; } // Export volume List<BlockObject> userAddedVolumes = new ArrayList<BlockObject>(); for (VolumeURIHLU volumeURIHLU : volumeURIHLUs) { final BlockObject blockObject = getBlockObject(volumeURIHLU.getVolumeURI()); final String volumeHLU = volumeURIHLU.getHLU(); if (volumeHLU != null && !volumeHLU.equalsIgnoreCase(ExportGroup.LUN_UNASSIGNED_STR)) { int hluDec = Integer.parseInt(volumeHLU, 16); if (hluDec > MAXIMUM_LUN) { String errMsg = String.format(INVALID_LUN_ERROR_MSG, hluDec, MAXIMUM_LUN); _log.error(errMsg); throw new Exception(errMsg); } else { restExportOpr.exportVolume(storageIP, exportType, exportName, blockObject.getLabel(), String.valueOf(hluDec), isSnapshot(volumeURIHLU.getVolumeURI())); userAddedVolumes.add(blockObject); } } } exportMask.addToUserCreatedVolumes(userAddedVolumes); _dbClient.updateObject(exportMask); taskCompleter.ready(_dbClient); } } catch (Exception e) { _log.error("Unexpected error: addVolume failed.", e); XIVRestException error = XIVRestException.exceptions.methodFailed("addVolume", e); taskCompleter.error(_dbClient, error); } _log.info("{} addVolume END...", storage.getLabel()); } /** * Removes volumes from an Export mask * * @param storage * Storage system instance * @param exportMaskURI * Export mask URI * @param volumeURIList * Volume to be removed URI * @param initiatorList * initiators to be valiated against for this operation * @param taskCompleter * task completer instance */ public void removeVolumesUsingREST(StorageSystem storage, URI exportMaskURI, List<URI> volumeURIList, List<Initiator> initiatorList, TaskCompleter taskCompleter) { try { // Export volume to Cluster if (volumeURIList != null && volumeURIList.size() > 0) { ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI); final String storageIP = storage.getSmisProviderIP(); XIVRestClient restExportOpr = getRestClient(storage); // Find HOST from Export Mask URI hostName = null; Set<Initiator> exportMaskInits = ExportMaskUtils.getInitiatorsForExportMask(_dbClient, exportMask, null); Iterator<Initiator> exportMaskInitsItr = exportMaskInits.iterator(); if (exportMaskInitsItr.hasNext()) { hostName = exportMaskInitsItr.next().getHost(); } final Host host = _dbClient.queryObject(Host.class, hostName); // Validate if it is a cluster String exportName = host.getLabel(); final String exportType = ExportMaskUtils.getExportType(_dbClient, exportMask); if (ExportGroup.ExportGroupType.Cluster.name().equals(exportType)) { Cluster cluster = _dbClient.queryObject(Cluster.class, host.getCluster()); exportName = cluster.getLabel(); } // Export volume for (URI volumeURI : volumeURIList) { final Volume volume = _dbClient.queryObject(Volume.class, volumeURI); if (volume != null) { restExportOpr.unExportVolume(storageIP, exportType, exportName, volume.getLabel()); } } } taskCompleter.ready(_dbClient); } catch (Exception e) { _log.error("Unexpected error: removeVolume failed.", e); XIVRestException error = XIVRestException.exceptions.methodFailed("removeVolume", e); taskCompleter.error(_dbClient, error); } } /** * Add Initiators to an existing Export Mask. * * @param storage * StorageSystem instance * @param exportMaskURI * Export mask URI where the initiator needs to be added * @param volumeURIList * Volume to be validated against this operation * @param initiatorList * List of initiators need to be added * @param taskCompleter * Task Completer instance */ public void addInitiatorsUsingREST(StorageSystem storage, URI exportMaskURI, List<URI> volumeURIs, List<Initiator> initiatorList, TaskCompleter taskCompleter) { try { ExportMask mask = _dbClient.queryObject(ExportMask.class, exportMaskURI); XIVRestClient restExportOpr = getRestClient(storage); final String storageIP = storage.getSmisProviderIP(); List<Initiator> userAddedInitiators = new ArrayList<Initiator>(); for (Initiator initiator : initiatorList) { final Host host = _dbClient.queryObject(Host.class, initiator.getHost()); // Add Initiators to Host. if (!restExportOpr.createHostPort(storageIP, host.getLabel(), Initiator.normalizePort(initiator.getInitiatorPort()), initiator.getProtocol().toLowerCase())) { userAddedInitiators.add(initiator); } } mask.addToUserCreatedInitiators(userAddedInitiators); ExportMaskUtils.sanitizeExportMaskContainers(_dbClient, mask); _dbClient.updateObject(mask); taskCompleter.ready(_dbClient); } catch (Exception e) { _log.error("Unexpected error: addInitiator failed.", e); XIVRestException error = XIVRestException.exceptions.methodFailed("addInitiator", e); taskCompleter.error(_dbClient, error); } } /** * Removes initiators from an existing export mask * * @param storage * StorageSystem instance * @param exportMaskURI * ExportMask URI where Initiator needs to be removed * @param volumeURIList * Volume to be validated against this operation * @param initiatorList * List of Initiators to be removed * @param taskCompleter * Task Completer instance */ public void removeInitiatorsUsingREST(StorageSystem storage, URI exportMaskURI, List<URI> volumeURIs, List<Initiator> initiatorList, TaskCompleter taskCompleter) { try { ExportMask mask = _dbClient.queryObject(ExportMask.class, exportMaskURI); XIVRestClient restExportOpr = getRestClient(storage); final String storageIP = storage.getSmisProviderIP(); List<URI> userRemovedInitiators = new ArrayList<URI>(); Set<URI> hostURIs = new HashSet<URI>(); if (null != initiatorList) { for (Initiator initiator : initiatorList) { final Host host = _dbClient.queryObject(Host.class, initiator.getHost()); final String normalizedPort = Initiator.normalizePort(initiator.getInitiatorPort()); if (restExportOpr.deleteHostPort(storageIP, host.getLabel(), normalizedPort, initiator.getProtocol().toLowerCase(), true)) { userRemovedInitiators.add(initiator.getId()); hostURIs.add(initiator.getHost()); } } } mask.removeFromUserAddedInitiatorsByURI(userRemovedInitiators); // Delete Host if there are no associated Initiators to it. for (URI hostURI : hostURIs) { Host host = _dbClient.queryObject(Host.class, hostURI); boolean hostDeleted = restExportOpr.deleteHost(storageIP, host.getLabel(), true); // Perform post-mask-delete cleanup steps if (hostDeleted) { unsetTag(host, storage.getSerialNumber()); } } ExportMaskUtils.sanitizeExportMaskContainers(_dbClient, mask); _dbClient.updateObject(mask); taskCompleter.ready(_dbClient); } catch (Exception e) { _log.error("Unexpected error: addInitiator failed.", e); XIVRestException error = XIVRestException.exceptions.methodFailed("addInitiator", e); taskCompleter.error(_dbClient, error); } } /** * Verify if volume is snapshot or not * * @param uri URI * @return */ private boolean isSnapshot(URI uri) { return URIUtil.isType(uri, BlockSnapshot.class); } }