/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.vplex.api; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.StringTokenizer; import org.codehaus.jettison.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.vplex.api.VPlexInitiatorInfo.Initiator_Type; import com.emc.storageos.vplex.api.clientdata.PortInfo; import com.sun.jersey.api.client.ClientResponse; /** * VPlexApiExportManager provides methods managing export and unexport operations. */ public class VPlexApiExportManager { private static final String RECOVERPOINT_INITIATOR_PREFIX = "500124"; // Logger reference. private static Logger s_logger = LoggerFactory.getLogger(VPlexApiExportManager.class); // A reference to the API client. private VPlexApiClient _vplexApiClient; /** * Package protected constructor. * * @param client A reference to the API client. */ VPlexApiExportManager(VPlexApiClient client) { _vplexApiClient = client; } /** * Creates a VPlex storage view so that the passed initiators have access to * the passed virtual volumes, via the passed target ports (i.e., the VPlex * front-end ports). Note that target ports are required to create a storage * view. Initiator ports and virtual volumes are optional and can be added * separately. * * @param viewName A unique name for the storage view. * @param targetPortInfo The info for the target ports. * @param initiatorPortInfo The info for the initiator ports. * @param virtualVolumeMap Map of virtual volume names to LUN ID. * * @return A reference to a VPlexStorageViewInfo specifying the storage view * information. * * @throws VPlexApiException When an error occurs creating the storage view. */ VPlexStorageViewInfo createStorageView(String viewName, List<PortInfo> targetPortInfo, List<PortInfo> initiatorPortInfo, Map<String, Integer> virtualVolumeMap) throws VPlexApiException { s_logger.info("Request to create storage view with name {}", viewName); // Find the target VPlex front end ports. List<VPlexTargetInfo> targetInfoList = new ArrayList<VPlexTargetInfo>(); VPlexClusterInfo clusterInfo = findTargets(targetPortInfo, targetInfoList, true); if (targetInfoList.size() != targetPortInfo.size()) { throw VPlexApiException.exceptions.failedToFindAllRequestedTargets(); } s_logger.info("Found targets ports for storage view"); // Create storage view with targets. createStorageView(viewName, clusterInfo, targetInfoList); s_logger.info("Storage view {} created", viewName); // Now we need to find the storage view we just created. VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); VPlexStorageViewInfo storageViewInfo = discoveryMgr.findStorageViewOnCluster( viewName, clusterInfo.getName(), false, true); if (storageViewInfo == null) { throw VPlexApiException.exceptions.couldNotFindStorageView(viewName); } s_logger.info("Found storage view"); // Find, register, and add the initiators when specified. if ((initiatorPortInfo != null) && !initiatorPortInfo.isEmpty()) { s_logger.info("Adding initiators to new storage view"); List<VPlexInitiatorInfo> initiatorInfoList = findInitiators(clusterInfo.getName(), initiatorPortInfo); if (initiatorInfoList.size() != initiatorPortInfo.size()) { s_logger.info("Could not find all of the requested initiators on VPlex."); initiatorInfoList = buildInitiatorInfoList(initiatorInfoList, initiatorPortInfo, clusterInfo); } // Register the initiators that are not registered. registerInitiators(clusterInfo, initiatorInfoList); s_logger.info("Registered initiators"); // Add the initiators to storage view. addStorageViewInitiators(storageViewInfo, initiatorInfoList); s_logger.info("Initiators added to new storage view"); } // Finally, add the virtual volumes to the storage view when specified. if ((virtualVolumeMap != null) && (virtualVolumeMap.size() != 0)) { s_logger.info("Adding virtual volumes to new storage view"); addStorageViewVirtualVolumes(storageViewInfo, virtualVolumeMap); s_logger.info("Virtual volumes added to new storage view"); } s_logger.info("Storage view {} creation was successful", viewName); return storageViewInfo; } /** * Delete the storage view with the passed name. * * @param viewName The name of the storage view to be deleted. * @param clusterName The name of the VPLEX cluster that the storage view is on. * @param viewFound An out parameter indicating whether or * not the storage view was actually found on * the VPLEX device during this process. * * @throws VPlexApiException When an error occurs deleting the storage view. */ void deleteStorageView(String viewName, String clusterName, Boolean[] viewFound) throws VPlexApiException { s_logger.info("Request to delete storage view {}", viewName); // Find the storage view with the passed name. VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); VPlexStorageViewInfo storageViewInfo = null; try { storageViewInfo = discoveryMgr.findStorageViewOnCluster(viewName, clusterName, false); } catch (Exception e) { s_logger.error("Exception trying to find VPLEX storage view.", e); viewFound[0] = false; throw e; } if (storageViewInfo == null) { s_logger.warn("Storage view {} not found. Nothing to delete.", viewName); viewFound[0] = false; return; } else { s_logger.info("Storage view {} was found on the VPLEX device.", viewName); viewFound[0] = true; } // Now delete it. ClientResponse response = null; try { // Build the destroy storage view request and make the request. URI requestURI = _vplexApiClient.getBaseURI().resolve( VPlexApiConstants.URI_DESTROY_STORAGE_VIEW); s_logger.info("Delete storage view URI is {}", requestURI.toString()); Map<String, String> argsMap = new HashMap<String, String>(); // assemble the full path to the storage view StringBuilder viewPath = new StringBuilder(); viewPath.append(VPlexApiConstants.URI_CLUSTERS_RELATIVE.toString()); viewPath.append(clusterName); viewPath.append(VPlexApiConstants.URI_STORAGE_VIEWS.toString()); viewPath.append(viewName); argsMap.put(VPlexApiConstants.ARG_DASH_V, viewPath.toString()); JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, true); s_logger.info("Delete storage view POST data is {}", postDataObject.toString()); response = _vplexApiClient.post(requestURI, postDataObject.toString()); String responseStr = response.getEntity(String.class); s_logger.info("Delete storage view response is {}", responseStr); if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) { if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) { s_logger.info("Storage view deletion completing asyncronously"); _vplexApiClient.waitForCompletion(response); } else { String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr); throw VPlexApiException.exceptions .deleteStorageViewFailureStatus(viewName, String.valueOf(response.getStatus()), cause); } } s_logger.info("Deleted storage view {}", viewName); } catch (VPlexApiException vae) { throw vae; } catch (Exception e) { throw VPlexApiException.exceptions.failedDeleteStorageView(viewName, e); } finally { if (response != null) { response.close(); } } } /** * Adds the initiators identified by the passed port information to the * storage view with the passed name. * * @param viewName The name of the storage view. * @param clusterName The name of the VPLEX cluster that the storage view is on. * @param initiatorsToAdd The port information for the initiators to be * added. * * @throws VPlexApiException When an error occurs adding the initiators. */ void addInitiatorsToStorageView(String viewName, String clusterName, List<PortInfo> initiatorsToAdd) throws VPlexApiException { // In case of a VPlex cross connect initiators will be in both the clusters. // So first find the storage view in both the VPlex clusters and then find // Initiators in a specific cluster. VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); VPlexStorageViewInfo storageViewInfo = discoveryMgr.findStorageViewOnCluster(viewName, clusterName, false); if (storageViewInfo == null) { throw VPlexApiException.exceptions.couldNotFindStorageView(viewName); } VPlexClusterInfo clusterInfo = discoveryMgr.findClusterInfo(storageViewInfo.getClusterId()); if (clusterInfo == null) { throw VPlexApiException.exceptions.couldNotFindCluster(storageViewInfo.getClusterId()); } List<VPlexInitiatorInfo> initiatorsFound = findInitiators(clusterName, initiatorsToAdd); if (initiatorsFound.size() != initiatorsToAdd.size()) { s_logger.info("Could not find all of the requested initiators on VPLex."); initiatorsFound = buildInitiatorInfoList(initiatorsFound, initiatorsToAdd, clusterInfo); } // Register the initiators that are not registered. registerInitiators(clusterInfo, initiatorsFound); // Add the initiators to storage view. addStorageViewInitiators(storageViewInfo, initiatorsFound); } /** * Adds additional targets (Vplex front end ports) to an existing Storage View. * * @param viewName -- The name of the existing Storage View. * @param targetPortInfo -- The port information for the target ports to be * added to the view. * @throws VPlexApiException When an error occurs adding the targets. */ void addTargetsToStorageView(String viewName, List<PortInfo> targetPortInfo) throws VPlexApiException { List<VPlexTargetInfo> targetInfoList = new ArrayList<VPlexTargetInfo>(); VPlexClusterInfo clusterInfo = findTargets(targetPortInfo, targetInfoList, true); if (targetInfoList.size() != targetPortInfo.size()) { throw VPlexApiException.exceptions.failedToFindAllRequestedTargets(); } s_logger.info("Found targets ports for storage view"); // Find the storage view. VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); VPlexStorageViewInfo storageViewInfo = discoveryMgr.findStorageViewOnCluster( viewName, clusterInfo.getName(), false); if (storageViewInfo == null) { throw VPlexApiException.exceptions.couldNotFindStorageView(viewName); } modifyStorageViewTargets(storageViewInfo, targetInfoList, false); } /** * Removes targets (Vplex front end ports) from an existing Storage View. * * @param viewName -- The name of the existing Storage View. * @param targetPortInfo -- The port information for the target ports to be * added to the view. * @throws VPlexApiException When an error occurs adding the targets. */ void removeTargetsFromStorageView(String viewName, List<PortInfo> targetPortInfo) throws VPlexApiException { List<VPlexTargetInfo> targetInfoList = new ArrayList<VPlexTargetInfo>(); VPlexClusterInfo clusterInfo = findTargets(targetPortInfo, targetInfoList, true); if (targetInfoList.size() != targetPortInfo.size()) { throw VPlexApiException.exceptions.failedToFindAllRequestedTargets(); } s_logger.info("Found targets ports for storage view"); // Find the storage view. VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); VPlexStorageViewInfo storageViewInfo = discoveryMgr.findStorageViewOnCluster( viewName, clusterInfo.getName(), false); if (storageViewInfo == null) { throw VPlexApiException.exceptions.couldNotFindStorageView(viewName); } modifyStorageViewTargets(storageViewInfo, targetInfoList, true); } /** * Removes the initiators identified by the passed port information from the * storage view with the passed name. * * @param viewName The name of the storage view. * @param clusterName The name of the VPLEX cluster that the storage view is on. * @param initiatorToRemove The port information for the initiators to be * removed. * * @throws VPlexApiException When an error occurs removing the initiators. */ void removeInitiatorsFromStorageView(String viewName, String clusterName, List<PortInfo> initiatorToRemove) throws VPlexApiException { // In case of a VPlex cross connect initiators will be in both the clusters. // So first find the storage view in both the VPlex clusters and then find // Initiators in a specific cluster. VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); VPlexStorageViewInfo storageViewInfo = discoveryMgr.findStorageViewOnCluster(viewName, clusterName, false); if (storageViewInfo == null) { throw VPlexApiException.exceptions.couldNotFindStorageView(viewName); } // Find the initiators in a cluster where storage view is found. List<VPlexInitiatorInfo> initiatorsFoundForRemoval = findInitiators(clusterName, initiatorToRemove); if (initiatorsFoundForRemoval.size() != initiatorToRemove.size()) { StringBuffer notFoundInitiators = new StringBuffer(); for (PortInfo portInfo : initiatorToRemove) { if (notFoundInitiators.length() == 0) { notFoundInitiators.append(portInfo.getPortWWN()); } else { notFoundInitiators.append(" ,").append(portInfo.getPortWWN()); } } throw VPlexApiException.exceptions.couldNotFindInitiators(notFoundInitiators.toString()); } // Remove the initiators from storage view. removeStorageViewInitiators(storageViewInfo, initiatorsFoundForRemoval); } /** * Adds the virtual volumes with the passed names to the storage view with * the passed name. * * @param viewName The name of the storage view. * @param clusterName The name of the VPLEX cluster that the storage view is on. * @param virtualVolumeMap Map of virtual volume names to LUN ID. * @return A reference to a VPlexStorageViewInfo specifying the storage view * information. * * @throws VPlexApiException When an error occurs adding the virtual * volumes. */ VPlexStorageViewInfo addVirtualVolumesToStorageView(String viewName, String clusterName, Map<String, Integer> virtualVolumeMap) throws VPlexApiException { // Find the virtual volume with the passed name. VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); VPlexStorageViewInfo storageViewInfo = discoveryMgr.findStorageViewOnCluster(viewName, clusterName, false); if (storageViewInfo == null) { throw VPlexApiException.exceptions.couldNotFindStorageView(viewName); } // Add the virtual volumes to the storage view. addStorageViewVirtualVolumes(storageViewInfo, virtualVolumeMap); return storageViewInfo; } /** * Removes the virtual volumes with the passed names from the storage view * with the passed name. * * @param viewName The name of the storage view. * @param clusterName The name of the VPLEX cluster that the storage view is on. * @param virtualVolumeNames The names of the virtual volumes to be removed. * * @throws VPlexApiException When an error occurs removing the virtual * volumes. */ void removeVirtualVolumesFromStorageView(String viewName, String clusterName, List<String> virtualVolumeNames) throws VPlexApiException { // Find the virtual volume with the passed name. VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); VPlexStorageViewInfo storageViewInfo = discoveryMgr.findStorageViewOnCluster(viewName, clusterName, true); if (storageViewInfo == null) { // if the storage view doesn't exist, there can't be any volumes in it // not an error; just return return; } // if the storage view is empty, return with no error; nothing to remove if (storageViewInfo.getVirtualVolumes() == null || storageViewInfo.getVirtualVolumes().isEmpty()) { return; } List<String> vvolsInSV = new ArrayList<String>(); for (String vplexVirtVol : storageViewInfo.getVirtualVolumes()) { s_logger.info("virtual volume from vplex: ", vplexVirtVol); StringTokenizer tokenizer = new StringTokenizer(vplexVirtVol, ","); // the virtual volume name is the second token if (tokenizer.countTokens() >= 2) { tokenizer.nextToken(); String vplexVirtVolName = tokenizer.nextToken(); vvolsInSV.add(vplexVirtVolName); } else { s_logger.warn("unexpected format for virtual volume " + vplexVirtVol + "; expecting a comma separated string with the volume name in the second token"); } } // exclude any volumes that aren't in the storage view List<String> volsToRemove = new ArrayList<String>(); for (String volToRemove : virtualVolumeNames) { if (vvolsInSV.contains(volToRemove)) { volsToRemove.add(volToRemove); } } if (!volsToRemove.isEmpty()) { // Remove the virtual volumes from the storage view. removeStorageViewVirtualVolumes(storageViewInfo, volsToRemove); } } /** * Finds the target FE ports corresponding to the ports identified by the * passed port information. * * @param targetPortInfo The target port information. * @param targetInfoList Out param containing the target information. * @param allTargetsOnSameCluster Whether or not all targets are on the same * cluster. * * @return The cluster on which the targets were found. * * @throws VPlexApiException When an error occurs attempting to find the * targets corresponding to the passed port information. */ private VPlexClusterInfo findTargets(List<PortInfo> targetPortInfo, List<VPlexTargetInfo> targetInfoList, boolean allTargetsOnSameCluster) throws VPlexApiException { VPlexClusterInfo targetClusterInfo = null; VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); List<VPlexClusterInfo> clusterInfoList = discoveryMgr.getClusterInfoLite(); for (VPlexClusterInfo clusterInfo : clusterInfoList) { List<VPlexTargetInfo> clusterTargetInfoList = discoveryMgr .getTargetInfoForCluster(clusterInfo.getName()); for (PortInfo portInfo : targetPortInfo) { String portWWN = portInfo.getPortWWN(); for (VPlexTargetInfo clusterTargetInfo : clusterTargetInfoList) { // TBD: Check node WWNs as well? The are optional. if (portWWN.equals(clusterTargetInfo.getPortWwn())) { targetInfoList.add(clusterTargetInfo); targetClusterInfo = clusterInfo; break; } } } // If all targets are on the same cluster and // if we found one, we are done. if ((allTargetsOnSameCluster) && (!targetInfoList.isEmpty())) { break; } } return targetClusterInfo; } /** * Finds the initiator ports on the VPlex corresponding to the ports * specified in the passed port information. Note that the functions * presumes that all initiators to be found are on the same VPLEX cluster. * This function will execute a cache clear and initiator discovery if some of the * initiators are not found. After the discovery it will then retry finding * the initiators that could not be found initially. * * @param clusterName the name of the cluster to check * @param initiatorsToFind a list of initiator port infos to update * @return a List of VPlexInitiatorInfos * * @throws VPlexApiException When an error occurs finding the initiators. */ List<VPlexInitiatorInfo> findInitiators(String clusterName, List<PortInfo> initiatorsToFind) throws VPlexApiException { List<VPlexInitiatorInfo> foundInitiators = findInitiatorsOnCluster(clusterName, initiatorsToFind); if (foundInitiators.size() != initiatorsToFind.size()) { // if some not found, retry once after clearing the initiator cache _vplexApiClient.getDiscoveryManager().clearInitiatorCache(clusterName); VPlexClusterInfo clusterInfo = _vplexApiClient.getClusterInfoLiteForClusterName(clusterName); _vplexApiClient.getDiscoveryManager().discoverInitiatorsOnCluster(clusterInfo); foundInitiators = findInitiatorsOnCluster(clusterName, initiatorsToFind); } return foundInitiators; } /** * Tries to find initiators on the the passed cluster corresponding to the * passed port information. * * @param clusterName The name of the cluster. * @param initiatorsToFind The port information. * * @return A list of VPlexInitiatorInfo representing the found initiators. * * @throws VPlexApiException When an error occurs finding initiators on the * cluster. */ private List<VPlexInitiatorInfo> findInitiatorsOnCluster(String clusterName, List<PortInfo> initiatorsToFind) throws VPlexApiException { List<VPlexInitiatorInfo> foundInitiators = new ArrayList<VPlexInitiatorInfo>(); // Get the initiators on the passed cluster. VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); List<VPlexInitiatorInfo> clusterInitiatorInfoList = discoveryMgr .getInitiatorInfoForCluster(clusterName, false); // Loop over the port information trying to match the port with // an initiator found on the cluster. for (PortInfo portInfo : initiatorsToFind) { String portWWN = portInfo.getPortWWN(); for (VPlexInitiatorInfo clusterInitiatorInfo : clusterInitiatorInfoList) { // TBD: Check node WWNs as well? They are optional. if (portWWN.equals(clusterInitiatorInfo.getPortWwn())) { foundInitiators.add(clusterInitiatorInfo); // Make sure a registration name is set. If the caller // passed a specific port name, then it was or will be // registered with that name. Otherwise, we give it a // default name. String portName = portInfo.getName(); if ((portName != null) && (portName.length() != 0)) { clusterInitiatorInfo.setRegistrationName(portName); } else { clusterInitiatorInfo .setRegistrationName(VPlexApiConstants.REGISTERED_INITIATOR_PREFIX + clusterInitiatorInfo.getPortWwnRaw()); } // Also, if the caller passed an initiator type make sure // it is set so that it can be passed when the initiator // is registered. Initiator_Type initiatorType = Initiator_Type.valueOfType(portInfo .getType()); if (initiatorType == null) { s_logger.info("Initiator port type {} not found, using default", portInfo.getType()); initiatorType = Initiator_Type.DEFAULT; } clusterInitiatorInfo.setInitiatorType(initiatorType); break; } } } return foundInitiators; } /** * Register the passed initiators if they have yet to be registered. * * @param clusterInfo The cluster on which the initiators are registered. * @param initiatorInfoList The initiators to be registered. * * @throws VPlexApiException When an error occurs registering an initiator. */ void registerInitiators(VPlexClusterInfo clusterInfo, List<VPlexInitiatorInfo> initiatorInfoList) throws VPlexApiException { for (VPlexInitiatorInfo initiatorInfo : initiatorInfoList) { // If the name of the initiator does not starts with // UNREGISTERED- then the initiator is already registered. if (!initiatorInfo.getName().startsWith( VPlexApiConstants.UNREGISTERED_INITIATOR_PREFIX)) { continue; } String initiatorName = initiatorInfo.getName(); s_logger.info("Registering initiator {}", initiatorName); ClientResponse response = null; try { // Build the value for the port argument for the request. StringBuilder argBuilder = new StringBuilder(); argBuilder.append(initiatorInfo.getPortWwnRaw()); String nodeWWN = initiatorInfo.getNodeWwnRaw(); String portWWN = initiatorInfo.getPortWwnRaw(); if ((nodeWWN != null) && (nodeWWN.length() != 0)) { argBuilder.append(VPlexApiConstants.INITIATOR_REG_DELIM); argBuilder.append(nodeWWN); } // Build the register initiator request and make the request. URI requestURI = _vplexApiClient.getBaseURI().resolve( VPlexApiConstants.URI_REGISTER_INITIATOR); s_logger.info("Register initiator URI is {}", requestURI.toString()); Map<String, String> argsMap = new HashMap<String, String>(); argsMap.put(VPlexApiConstants.ARG_DASH_I, initiatorInfo.getRegistrationName()); argsMap.put(VPlexApiConstants.ARG_DASH_P, argBuilder.toString()); argsMap.put(VPlexApiConstants.ARG_DASH_C, clusterInfo.getPath()); if (isRecoverPointInitiator(portWWN)) { argsMap.put(VPlexApiConstants.ARG_DASH_T, "recoverpoint"); } Initiator_Type initiatorType = initiatorInfo.getInitiatorType(); if (initiatorType != Initiator_Type.DEFAULT) { argsMap.put(VPlexApiConstants.ARG_DASH_T, initiatorType.getType()); } JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false); s_logger.info("Register initiator POST data is {}", postDataObject.toString()); response = _vplexApiClient.post(requestURI, postDataObject.toString()); String responseStr = response.getEntity(String.class); s_logger.info("Register initiator response is {}", responseStr); if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) { if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) { s_logger.info("Initiator registration completing asynchronously"); _vplexApiClient.waitForCompletion(response); } else { String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr); throw VPlexApiException.exceptions .registerInitiatorFailureStatus(initiatorName, String.valueOf(response.getStatus()), cause); } } // Update the initiator info to reflect the registration name // for the initiator. initiatorInfo.updateOnRegistration(); s_logger.info(String.format("Successfully registered initiator %s", initiatorInfo.getName())); } catch (VPlexApiException vae) { throw vae; } catch (Exception e) { throw VPlexApiException.exceptions.failedRegisterInitiator(initiatorName, e); } finally { if (response != null) { response.close(); } } } } /** * Determines if the given port WWN is a RecoverPoint initiator. * * @param portWWN the port WWN * @return true if the WWN is a RecoverPoint initiator, false otherwise. */ private boolean isRecoverPointInitiator(String portWWN) { // TODO: RP-TEAM - this is NOT how we should be determing if the initiator type is RP. // the error that i got when i tried to register an initiator said something like the portWWN contains EMC Recoverpoint Vendor ID. // I wasnt able to decipher any kind of info from that as the portWWN is nothing but a WWN. // What we can possibly do here ? - I think we can tinker with the VplexInitiatorInfo class and add "type" field in there to make // this more cleaner but i am really not sure // if that would give anything. From experience with the initiators registration on the VPLEX manually, i dont even think that there // is way to find out from the VPLEX UI other // than the fact that "we" know that any initiator that begins with the prefix below is an RP initiator. Something to investigate. return portWWN.contains(RECOVERPOINT_INITIATOR_PREFIX); } /** * Create a storage view with the passed name on the passed cluster * containing the passed targets. * * @param viewName The name for the storage view. * @param clusterInfo The cluster on which to create the storage view. * @param targetInfoList The list of targets to be in the storage view. * * @throws VPlexApiException When an error occurs creating the storage view. */ private void createStorageView(String viewName, VPlexClusterInfo clusterInfo, List<VPlexTargetInfo> targetInfoList) throws VPlexApiException { boolean retryNeeded = false; int retryCount = 0; do { retryNeeded = false; ClientResponse response = null; try { // Create the value for the target ports argument for the request. StringBuilder targetPathBuilder = new StringBuilder(); for (VPlexTargetInfo targetInfo : targetInfoList) { if (targetPathBuilder.length() != 0) { targetPathBuilder.append(","); } targetPathBuilder.append(targetInfo.getPath()); } s_logger.info("Creating storage view {} with targets {}", viewName, targetPathBuilder.toString()); // Build the create storage view request and make the request. URI requestURI = _vplexApiClient.getBaseURI().resolve( VPlexApiConstants.URI_CREATE_STORAGE_VIEW); s_logger.info("Create storage view URI is {}", requestURI.toString()); Map<String, String> argsMap = new HashMap<String, String>(); argsMap.put(VPlexApiConstants.ARG_DASH_N, viewName); argsMap.put(VPlexApiConstants.ARG_DASH_P, targetPathBuilder.toString()); argsMap.put(VPlexApiConstants.ARG_DASH_C, clusterInfo.getPath()); JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false); s_logger.info("Create storage view POST data is {}", postDataObject.toString()); response = _vplexApiClient.post(requestURI, postDataObject.toString()); String responseStr = response.getEntity(String.class); s_logger.info("Create storage view response is {}", responseStr); if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) { if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) { s_logger.info("Storage view creation completing asynchronously"); _vplexApiClient.waitForCompletion(response); } else if (response.getStatus() == VPlexApiConstants.COULD_NOT_READ_STORAGE_VIEW_STATUS && retryCount++ < VPlexApiConstants.STORAGE_VIEW_CREATE_MAX_RETRIES) { s_logger.info("VPlex error {} will retry after a delay", response.getStatus()); VPlexApiUtils.pauseThread(VPlexApiConstants.STORAGE_VIEW_CREATE_RETRY_TIME_MS); retryNeeded = true; } else { String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr); throw VPlexApiException.exceptions .createStorageViewFailureStatus(viewName, String.valueOf(response.getStatus()), cause); } } if (!retryNeeded) { s_logger.info("Created storage view {}", viewName); } } catch (VPlexApiException vae) { throw vae; } catch (Exception e) { throw VPlexApiException.exceptions.failedCreateStorageView(viewName, e); } finally { if (response != null) { response.close(); } } } while (retryNeeded); } /** * Adds the passed initiators to the storage view. * * @param storageViewInfo The storage view. * @param initiatorInfoList The initiators to be added to the storage view. * * @throws VPlexApiException When an error occurs adding the initiators to * the storage view. */ private void addStorageViewInitiators(VPlexStorageViewInfo storageViewInfo, List<VPlexInitiatorInfo> initiatorInfoList) throws VPlexApiException { URI requestURI = _vplexApiClient.getBaseURI().resolve( VPlexApiConstants.URI_STORAGE_VIEW_ADD_INITIATORS); modifyStorageViewInitiators(storageViewInfo, initiatorInfoList, requestURI); } /** * Removes the passed initiators from the storage view. * * @param storageViewInfo The storage view. * @param initiatorInfoList The initiators to be removed from the storage * view. * * @throws VPlexApiException When an error occurs removing the initiators * from the storage view. */ private void removeStorageViewInitiators(VPlexStorageViewInfo storageViewInfo, List<VPlexInitiatorInfo> initiatorInfoList) throws VPlexApiException { URI requestURI = _vplexApiClient.getBaseURI().resolve( VPlexApiConstants.URI_STORAGE_VIEW_REMOVE_INITIATORS); modifyStorageViewInitiators(storageViewInfo, initiatorInfoList, requestURI); } /** * Adds or removes the passed initiators to/from the passed storage view * according to the passed request URI. * * @param storageViewInfo The storage view. * @param initiatorInfoList The initiators to be added/removed to/from the * storage view. * @param requestURI The URI for the modification request. * * @throws VPlexApiException When an error occurs modifying the initiators * for the storage view. */ private void modifyStorageViewInitiators(VPlexStorageViewInfo storageViewInfo, List<VPlexInitiatorInfo> initiatorInfoList, URI requestURI) throws VPlexApiException { ClientResponse response = null; try { // Create the value for the initiators argument for the request. StringBuilder initiatorPathBuilder = new StringBuilder(); for (VPlexInitiatorInfo initiatorInfo : initiatorInfoList) { if (initiatorPathBuilder.length() != 0) { initiatorPathBuilder.append(","); } initiatorPathBuilder.append(initiatorInfo.getPath()); } // Build the request post data and make the request. s_logger.info("Modify storage view initiators request URI is {}", requestURI.toString()); Map<String, String> argsMap = new HashMap<String, String>(); argsMap.put(VPlexApiConstants.ARG_DASH_I, initiatorPathBuilder.toString()); argsMap.put(VPlexApiConstants.ARG_DASH_V, storageViewInfo.getPath()); JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false); s_logger.info("Storage view update initiators POST data is {}", postDataObject.toString()); response = _vplexApiClient.post(requestURI, postDataObject.toString()); String responseStr = response.getEntity(String.class); s_logger.info("Storage view initiator response is {}", responseStr); if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) { if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) { s_logger.info("Storage view initiator changes completing asynchronously"); _vplexApiClient.waitForCompletion(response); } else { String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr); throw VPlexApiException.exceptions .modifyViewInitiatorsFailureStatus(storageViewInfo.getName(), String.valueOf(response.getStatus()), cause); } } s_logger.info("Successfully updated initiators for storage view {}", storageViewInfo.getName()); } catch (VPlexApiException vae) { throw vae; } catch (Exception e) { throw VPlexApiException.exceptions.failedModifyViewInitiators( storageViewInfo.getName(), e); } finally { if (response != null) { response.close(); } } } /** * Adds or removes targets (VPLEX Ports) to a StorageView. * * @param storageViewInfo -- has the StorageView path * @param targetInfoList -- The list of targets to be added/removed * @param remove -- Boolean, if true, will remove, otherwise add the targets. */ private void modifyStorageViewTargets(VPlexStorageViewInfo storageViewInfo, List<VPlexTargetInfo> targetInfoList, boolean remove) { ClientResponse response = null; try { URI requestURI = (remove ? _vplexApiClient.getBaseURI().resolve(VPlexApiConstants.URI_STORAGE_VIEW_REMOVE_TARGETS) : _vplexApiClient.getBaseURI().resolve(VPlexApiConstants.URI_STORAGE_VIEW_ADD_TARGETS)); // Create the value for the target ports argument for the request. StringBuilder targetPathBuilder = new StringBuilder(); for (VPlexTargetInfo targetInfo : targetInfoList) { if (targetPathBuilder.length() != 0) { targetPathBuilder.append(","); } targetPathBuilder.append(targetInfo.getPath()); } // Build the request post data and make the request. s_logger.info("modifyStorageViewTargets URI is {}", requestURI.toString()); Map<String, String> argsMap = new HashMap<String, String>(); argsMap.put(VPlexApiConstants.ARG_DASH_V, storageViewInfo.getPath()); argsMap.put(VPlexApiConstants.ARG_DASH_P, targetPathBuilder.toString()); JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false); s_logger.info("Modify Storage View Targets POST data is {}", postDataObject.toString()); response = _vplexApiClient.post(requestURI, postDataObject.toString()); String responseStr = response.getEntity(String.class); s_logger.info("Modify storage view response is {}", responseStr); if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) { if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) { s_logger.info("Modify Storage View Targets completing asynchronously"); _vplexApiClient.waitForCompletion(response); } else { String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr); throw VPlexApiException.exceptions .modifyViewTargetsFailureStatus(storageViewInfo.getName(), String.valueOf(response.getStatus()), cause); } } if (remove) { s_logger.info("Removed targets from storage view {}", storageViewInfo.getName()); } else { s_logger.info("Added targets to storage view {}", storageViewInfo.getName()); } } catch (VPlexApiException vae) { throw vae; } catch (Exception e) { throw VPlexApiException.exceptions.failedModifyViewTargets( storageViewInfo.getName(), e); } finally { if (response != null) { response.close(); } } } /** * Adds the virtual volumes with the passed names to the passed storage * view. * * @param storageViewInfo The storage view. * @param virtualVolumeMap Map of virtual volume names to LUN ID. * * @throws VPlexApiException When an errors occurs adding the virtual * volumes to the storage view. */ private void addStorageViewVirtualVolumes(VPlexStorageViewInfo storageViewInfo, Map<String, Integer> virtualVolumeMap) throws VPlexApiException { URI requestURI = _vplexApiClient.getBaseURI().resolve( VPlexApiConstants.URI_STORAGE_VIEW_ADD_VOLUMES); // Create the value for the volumes argument for the request. StringBuilder volumeArgsBuilder = new StringBuilder(); Iterator<Entry<String, Integer>> virtualVolumesIter = virtualVolumeMap.entrySet().iterator(); while (virtualVolumesIter.hasNext()) { Entry<String, Integer> entry = virtualVolumesIter.next(); String virtualVolumeName = entry.getKey(); Integer lunId = entry.getValue(); if (volumeArgsBuilder.length() != 0) { volumeArgsBuilder.append(","); } // If no LUN ID is assigned, we simply use the name, otherwise // we include the requested LUN ID in the argument. if (lunId.intValue() == VPlexApiConstants.LUN_UNASSIGNED) { volumeArgsBuilder.append(virtualVolumeName); } else { volumeArgsBuilder.append("("); volumeArgsBuilder.append(lunId); volumeArgsBuilder.append(","); volumeArgsBuilder.append(virtualVolumeName); volumeArgsBuilder.append(")"); } } modifyStorageViewVirtualVolumes(storageViewInfo, volumeArgsBuilder.toString(), requestURI); // updateStorageViewInfo(storageViewInfo); Iterator<String> virtualVolumesNamesIter = virtualVolumeMap.keySet().iterator(); while (virtualVolumesNamesIter.hasNext()) { String virtualVolumeName = virtualVolumesNamesIter.next(); s_logger.info("WWN {} for Volume {}", storageViewInfo.getWWNForStorageViewVolume(virtualVolumeName), virtualVolumeName); } } /** * Updates a VPlexStorageViewInfo object with detailed attributes. * * @param storageViewInfo */ private void updateStorageViewInfo(VPlexStorageViewInfo storageViewInfo) { VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager(); discoveryMgr.updateStorageViewInfo(storageViewInfo); } /** * Removes the virtual volumes with the passed names from the passed storage * view. * * @param storageViewInfo The storage view. * @param virtualVolumeNames The names of the virtual volumes to be removed. * * @throws VPlexApiException When an errors occurs removing the virtual * volumes to the storage view. */ private void removeStorageViewVirtualVolumes(VPlexStorageViewInfo storageViewInfo, List<String> virtualVolumeNames) throws VPlexApiException { URI requestURI = _vplexApiClient.getBaseURI().resolve( VPlexApiConstants.URI_STORAGE_VIEW_REMOVE_VOLUMES); // Create the value for the volumes argument for the request. StringBuilder volumeNamesBuilder = new StringBuilder(); for (String virtualVolumename : virtualVolumeNames) { if (volumeNamesBuilder.length() != 0) { volumeNamesBuilder.append(","); } volumeNamesBuilder.append(virtualVolumename); } modifyStorageViewVirtualVolumes(storageViewInfo, volumeNamesBuilder.toString(), requestURI); } /** * Adds or removes the passed virtual volumes to/from the passed storage * view according to the passed request URI. * * @param storageViewInfo The storage view. * @param virtualVolumesArg The value for the volumes argument for the request. * @param requestURI The URI for the modification request. * * @throws VPlexApiException When an error occurs modifying the virtual * volumes for the storage view. */ private void modifyStorageViewVirtualVolumes(VPlexStorageViewInfo storageViewInfo, String virtualVolumesArg, URI requestURI) throws VPlexApiException { ClientResponse response = null; try { // Build the request post data and make the request. s_logger.info("Modify storage view virtual volumes request URI is {}", requestURI.toString()); Map<String, String> argsMap = new HashMap<String, String>(); argsMap.put(VPlexApiConstants.ARG_DASH_O, virtualVolumesArg); argsMap.put(VPlexApiConstants.ARG_DASH_V, storageViewInfo.getPath()); JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, true); s_logger.info("Storage view modify virtual volumes POST data is {}", postDataObject.toString()); response = _vplexApiClient.post(requestURI, postDataObject.toString()); String responseStr = response.getEntity(String.class); s_logger.info("Storage view modify virtual volumes response is {}", responseStr); if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) { if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) { s_logger.info("Storage view volume changes completing asynchronously"); _vplexApiClient.waitForCompletion(response); } else { String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr); throw VPlexApiException.exceptions .modifyViewVolumesFailureStatus(storageViewInfo.getName(), String.valueOf(response.getStatus()), cause); } } s_logger.info("Successfully updated volumes for storage view {}", storageViewInfo.getName()); } catch (VPlexApiException vae) { throw vae; } catch (Exception e) { throw VPlexApiException.exceptions.failedModifyViewVolumes( storageViewInfo.getName(), e); } finally { if (response != null) { response.close(); } } } /** * Gets the target info for the passed ports. * * @param portInfoList The list of ports. * * @return A map of the associated target info for the ports keyed by the * port WWN. * * @throws VPlexApiException When an error occurs getting the target info. */ public Map<String, VPlexTargetInfo> getTargetInfoForPorts( List<VPlexPortInfo> portInfoList) throws VPlexApiException { // There will only be targets for FE ports. Ignore ports that are // not FE ports. List<PortInfo> fePortInfoList = new ArrayList<PortInfo>(); for (VPlexPortInfo portInfo : portInfoList) { if (portInfo.isFrontendPort()) { PortInfo fePortInfo = new PortInfo(portInfo.getPortWwn()); fePortInfoList.add(fePortInfo); } } // Get the target info for these FE ports. List<VPlexTargetInfo> targetInfoList = new ArrayList<VPlexTargetInfo>(); findTargets(fePortInfoList, targetInfoList, false); // Now map the target info for each FE port to the // port WWN of that port. Map<String, VPlexTargetInfo> targetInfoMap = new HashMap<String, VPlexTargetInfo>(); for (VPlexTargetInfo targetInfo : targetInfoList) { targetInfoMap.put(targetInfo.getPortWwn(), targetInfo); } return targetInfoMap; } /** * This methods builds the VPlexInitiatorInfo for the initiators which does not * exist on VPlex and returns the list of VPlexInitiatorInfo for all the initiators. * * @param alreadyFoundInitiatorInfoList List of initiators that exist on VPlex * @param initiatorPortInfo All the initiators that needs to be registered on VPlex * @param clusterInfo The VPlex cluster info for the VPlex cluster where initiators * should be registered * * @return List of VPlexInitiatorInfo that includes all the initiators that needs * to be registered and the initiators which already exist on VPlex */ List<VPlexInitiatorInfo> buildInitiatorInfoList(List<VPlexInitiatorInfo> alreadyFoundInitiatorInfoList, List<PortInfo> initiatorPortInfo, VPlexClusterInfo clusterInfo) { List<VPlexInitiatorInfo> initiatorInfoList = new ArrayList<VPlexInitiatorInfo>(); // Create map by pwwn of the already found initiators on VPLEX. Map<String, VPlexInitiatorInfo> initiatorInfoMap = new HashMap<String, VPlexInitiatorInfo>(); for (VPlexInitiatorInfo initiatorInfo : alreadyFoundInitiatorInfoList) { initiatorInfoMap.put(initiatorInfo.getPortWwn(), initiatorInfo); } // Iterate over all the requested initiatorPortInfo to build VPlexInitiatorInfo if its not // found on the VPLEX. for (PortInfo initiatorInfo : initiatorPortInfo) { if (initiatorInfoMap.get(initiatorInfo.getPortWWN()) != null) { initiatorInfoList.add(initiatorInfoMap.get(initiatorInfo.getPortWWN())); } else { // This initiator does not exist on VPlex. Create VPlexInitiatorInfo for it. s_logger.info("Creating VPlexInitiatorInfo for the initiator :" + initiatorInfo.getPortWWN()); VPlexInitiatorInfo info = new VPlexInitiatorInfo(); info.setRegistrationName(VPlexApiConstants.REGISTERED_INITIATOR_PREFIX + VPlexApiConstants.WWN_PREFIX + initiatorInfo.getPortWWN().toLowerCase()); info.setName(VPlexApiConstants.UNREGISTERED_INITIATOR_PREFIX + VPlexApiConstants.WWN_PREFIX + initiatorInfo.getPortWWN().toLowerCase()); info.setPortWwn(VPlexApiConstants.WWN_PREFIX + initiatorInfo.getPortWWN().toLowerCase()); info.setNodeWwn(VPlexApiConstants.WWN_PREFIX + initiatorInfo.getNodeWWN().toLowerCase()); if (initiatorInfo.getType() != null && !initiatorInfo.getType().isEmpty()) { info.setInitiatorType(Initiator_Type.valueOfType(initiatorInfo.getType())); } info.setPath(clusterInfo.getPath() + VPlexApiConstants.URI_INITIATORS + info.getName()); initiatorInfoList.add(info); } } return initiatorInfoList; } }