/*
* 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.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import com.emc.storageos.vplex.api.clientdata.VolumeInfo;
import com.sun.jersey.api.client.ClientResponse;
/**
* VPlexApiDiscoveryManger provides methods for discovering and collecting
* information from the VPlex.
*/
public class VPlexApiDiscoveryManager {
// Logger reference.
private static Logger s_logger = LoggerFactory.getLogger(VPlexApiDiscoveryManager.class);
// A reference to the API client.
private VPlexApiClient _vplexApiClient;
// initiator info cache by cluster name
private volatile Map<String, List<VPlexInitiatorInfo>> _vplexClusterInitiatorInfoCache = new HashMap<String, List<VPlexInitiatorInfo>>();
/**
* Package protected constructor.
*
* @param client A reference to the API client.
*/
VPlexApiDiscoveryManager(VPlexApiClient client) {
_vplexApiClient = client;
}
/**
* Returns the version of the VPlex management software.
*
* @return The version of the VPlex management software.
*
* @throws VPlexApiException When an error occurs getting the version information.
*/
public String getManagementSoftwareVersion() throws VPlexApiException {
String version = null;
// Get the URI for the management server info request and make the request.
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_VERSION_INFO);
s_logger.info("Mangement software version request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.post(requestURI, "");
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Get management server version is completing asynchronously");
responseStr = _vplexApiClient.waitForCompletion(response);
s_logger.info("Task Response is {}", responseStr);
} else {
throw VPlexApiException.exceptions
.failedGettingVPLEXMgmntSvrVersionStatus(String.valueOf(status));
}
}
// Now parse this response to extract the custom data, which
// contains the version.
try {
String customData = VPlexApiUtils.getCustomDataFromResponse(responseStr);
s_logger.info("Custom data is {}", customData);
String[] versionSubStrs = customData.split("Product Version");
String versionSubString = versionSubStrs[1];
versionSubString = versionSubString.substring(0, versionSubString.indexOf("-"));
version = versionSubString.trim();
} catch (Exception e) {
throw VPlexApiException.exceptions.failedProcessingMgmntSvrVersionFromResponse(e);
}
return version;
}
/**
* Determines the serial number of the VPlex management server.
*
* @return The serial number of the VPlex management server.
*
* @throws VPlexApiException When an error occurs querying the VPlex.
*/
String getManagementServerSerialNumber() throws VPlexApiException {
// Get the URI for the management server info request and make the request.
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_MANAGEMENT_SERVER);
s_logger.info("Management Server Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(String.format(
"Failed getting management server info with status: %s",
status));
}
// Now parse this response to extract the attributes.
try {
Map<String, Object> attributes = VPlexApiUtils.getAttributesFromResponse(responseStr);
return attributes.get(VPlexApiConstants.SERIAL_NO_ATT_KEY).toString();
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing management server information: %s", e.getMessage()), e);
}
}
/**
* Rediscovers the storage systems attached to the VPlex identified by the
* passed identifiers for the purpose of discovering new volumes accessible
* to the VPlex.
*
* @param storageSystemNativeGuids The native guids of the storage systems
* to be rediscovered.
*/
void rediscoverStorageSystems(List<String> storageSystemNativeGuids)
throws VPlexApiException {
List<VPlexStorageSystemInfo> storageSystemInfoList = getStorageSystemInfo();
Iterator<String> storageSystemIter = storageSystemNativeGuids.iterator();
while (storageSystemIter.hasNext()) {
boolean foundSystem = false;
String storageSystemNativeGuid = storageSystemIter.next();
for (VPlexStorageSystemInfo storageSystemInfo : storageSystemInfoList) {
if (!storageSystemInfo.matches(storageSystemNativeGuid)) {
continue;
}
// Found the storage system, rediscover it.
foundSystem = true;
ClientResponse response = null;
try {
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_REDISCOVER_ARRAY);
s_logger.info("Rediscover storage system URI is {}", requestURI.toString());
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_A, storageSystemInfo.getPath());
argsMap.put(VPlexApiConstants.ARG_DASH_C, storageSystemInfo.getClusterId());
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, true);
s_logger.info("Rediscover system POST data is {}", postDataObject.toString());
response = _vplexApiClient.post(requestURI,
postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Rediscover response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
_vplexApiClient.waitForCompletion(response);
} else {
throw new VPlexApiException(
String.format("Request to rediscover storage systems failed with Status: %s",
response.getStatus()));
}
}
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Exception redsicovering storage systems: %s", e.getMessage()), e);
} finally {
if (response != null) {
response.close();
}
}
}
if (!foundSystem) {
throw new VPlexApiException(String.format(
"Could not find storage system %s", storageSystemNativeGuid));
}
}
}
/**
* Gets the information for the storage systems accessible by the VPlex.
*
* @return A list of VPlexStorageSystemInfo specifying the info for the
* storage systems accessible to the VPlex.
*
* @throws VPlexApiException If a VPlex request returns a failed status or
* an error occurs processing the response.
*/
List<VPlexStorageSystemInfo> getStorageSystemInfo() throws VPlexApiException {
List<VPlexStorageSystemInfo> storageSystemInfoList = new ArrayList<VPlexStorageSystemInfo>();
List<VPlexClusterInfo> clusterInfoList = getClusterInfoLite();
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
List<VPlexStorageSystemInfo> clusterStorageSystemInfoList = getStorageSystemInfoForCluster(clusterInfo
.getName());
// We used to have a check here that only added an array once, even if it is in both clusters.
// However in our lab environment, it seems to often occur that an array will be connected
// to both clusters. If we don't include it both times, we would be unable to discover
// storage-volumes on it in cluster-2 because cluster-2 would not issue the rediscover command.
for (VPlexStorageSystemInfo storageSystemInfo : clusterStorageSystemInfoList) {
storageSystemInfoList.add(storageSystemInfo);
}
}
return storageSystemInfoList;
}
/**
* Gets the information for the VPlex Ports.
*
* @return A list of VPlexPortInfo specifying the info for the VPlex ports.
*
* @throws VPlexApiException If a VPlex request returns a failed status or
* an error occurs processing the response.
*/
List<VPlexPortInfo> getPortAndDirectorInfo() throws VPlexApiException {
List<VPlexPortInfo> portInfoList = new ArrayList<VPlexPortInfo>();
for (VPlexEngineInfo engineInfo : getEngineInfo()) {
for (VPlexDirectorInfo directorInfo : engineInfo.getDirectorInfo()) {
for (VPlexPortInfo portInfo : directorInfo.getPortInfo()) {
portInfoList.add(portInfo);
}
}
}
return portInfoList;
}
/**
* Gets all the storage port info for a VPLEX device.
*
* @return a list of VPlexPortInfo objects for the VPLEX.
*
* @throws VPlexApiException
*/
List<VPlexPortInfo> getPortInfo() throws VPlexApiException {
s_logger.info("Getting all port information from VPLEX at "
+ _vplexApiClient.getBaseURI().toString());
// Get the URI for the port info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_ENGINES.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
uriBuilder.append(VPlexApiConstants.URI_DIRECTORS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
uriBuilder.append(VPlexApiConstants.URI_DIRECTOR_PORTS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Director Ports Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI, VPlexApiConstants.ACCEPT_JSON_FORMAT_1);
String responseStr = response.getEntity(String.class);
int status = response.getStatus();
response.close();
if (status == VPlexApiConstants.SUCCESS_STATUS) {
try {
return VPlexApiUtils.getResourcesFromResponseContext(uriBuilder.toString(),
responseStr, VPlexPortInfo.class);
} catch (Exception e) {
throw VPlexApiException.exceptions.errorProcessingPortInformation(e.getLocalizedMessage());
}
} else if (status == VPlexApiConstants.NOT_FOUND_STATUS) {
// return an empty list rather than an error
s_logger.info("VPLEX returned a 404 Not Found for this context, returning an empty list instead.");
return new ArrayList<VPlexPortInfo>();
} else {
throw VPlexApiException.exceptions.failedGettingPortInfo(String.valueOf(status));
}
}
/**
* Finds the info for the VPlex cluster by cluster name.
*
* @param clusterName The VPlex cluster name.
* @return VPlexClusterInfo specifying the info for the VPlex
* clusters.
*/
public VPlexClusterInfo findClusterInfo(String clusterName) {
VPlexClusterInfo vplexclusterInfo = null;
VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager();
List<VPlexClusterInfo> clusterInfoList = discoveryMgr.getClusterInfoLite();
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
if (clusterInfo.getName().equals(clusterName)) {
vplexclusterInfo = clusterInfo;
break;
}
}
return vplexclusterInfo;
}
/**
* Gets the information for the VPlex Clusters
*
* @param shallow true to get just the name and path for each cluster, false
* to get additional info about the systems and volumes.
* @param getStorageVolumeAtts true to get the storage volume attributes, false otherwise.
* @param clusterToGet if non-null, will only return VPlexClusterInfo for the named cluster.
*
* @return A list of VPlexClusterInfo specifying the info for the VPlex
* clusters.
*
* @throws VPlexApiException If a VPlex request returns a failed status or
* an error occurs processing the response.
*/
List<VPlexClusterInfo> getClusterInfo(boolean shallow, boolean getStorageVolumeAtts, String clusterToGet)
throws VPlexApiException {
// Get the URI for the cluster info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Clusters Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI, VPlexApiConstants.ACCEPT_JSON_FORMAT_1);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.failedToGetClusterInfo(String.valueOf(status));
}
// Successful Response
try {
List<VPlexClusterInfo> clusterInfoList = VPlexApiUtils.getResourcesFromResponseContext(
VPlexApiConstants.URI_CLUSTERS.toString(), responseStr,
VPlexClusterInfo.class);
if (null != clusterToGet) {
Iterator<VPlexClusterInfo> clusterInfoIterator = clusterInfoList.iterator();
while (clusterInfoIterator.hasNext()) {
VPlexClusterInfo clusterInfo = clusterInfoIterator.next();
if (!clusterToGet.equals(clusterInfo.getName())) {
clusterInfoIterator.remove();
}
}
}
if (!shallow) {
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
String clusterName = clusterInfo.getName();
clusterInfo.setStorageSystemInfo(getStorageSystemInfoForCluster(clusterName));
clusterInfo.setSystemVolumeInfo(getSystemVolumeInfoForCluster(clusterName));
clusterInfo.setStorageVolumeInfo(getStorageVolumeInfoForCluster(clusterName, getStorageVolumeAtts));
}
}
return clusterInfoList;
} catch (Exception e) {
s_logger.error(e.getLocalizedMessage(), e);
throw VPlexApiException.exceptions.errorProcessingClusterInfo(e.getLocalizedMessage());
}
}
/**
* Gets the basic information for the VPlex Clusters. This will include
* the cluster name, type, and context path only.
*
* @return A list of VPlexClusterInfo specifying the info for the VPlex
* clusters.
*
* @throws VPlexApiException If a VPlex request returns a failed status or
* an error occurs processing the response.
*/
List<VPlexClusterInfo> getClusterInfoLite()
throws VPlexApiException {
// Get the URI for the cluster info request and make the request.
URI requestURI = _vplexApiClient.getBaseURI().resolve(VPlexApiConstants.URI_CLUSTERS);
s_logger.info("Clusters Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.failedToGetClusterInfo(String.valueOf(status));
}
// Successful Response
try {
List<VPlexClusterInfo> clusterInfoList = VPlexApiUtils.getChildrenFromResponse(
VPlexApiConstants.URI_CLUSTERS.toString(), responseStr,
VPlexClusterInfo.class);
return clusterInfoList;
} catch (Exception e) {
throw VPlexApiException.exceptions.errorProcessingClusterInfo(e.getLocalizedMessage());
}
}
/**
* Finds the volumes in the VPlex configuration identified by the passed
* native volume information.
*
* @param volumeInfoList The native volume info for the volumes to find.
* @param clusterInfoList The cluster information.
*
* @return A map of the found VPlex volumes, key'd by the native volume
* information for each volume.
*
* @throws VPlexApiException When an error occurs find the volumes.
*/
Map<VolumeInfo, VPlexStorageVolumeInfo> findStorageVolumes(List<VolumeInfo> volumeInfoList,
List<VPlexClusterInfo> clusterInfoList)
throws VPlexApiException {
Map<VolumeInfo, VPlexStorageVolumeInfo> storageVolumeInfoMap = new HashMap<VolumeInfo, VPlexStorageVolumeInfo>();
Iterator<VolumeInfo> volumeInfoIter = volumeInfoList.iterator();
while (volumeInfoIter.hasNext()) {
boolean volumeFound = false;
VolumeInfo volumeInfo = volumeInfoIter.next();
String storageSystemNativeGuid = volumeInfo.getStorageSystemNativeGuid();
String volumeWWN = volumeInfo.getVolumeWWN().toLowerCase();
s_logger.info("Volume WWN is {}", volumeWWN);
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
if (clusterInfo.containsStorageSystem(storageSystemNativeGuid)) {
s_logger.info("Found storage system {} in cluster {}", storageSystemNativeGuid, clusterInfo.getName());
VPlexStorageVolumeInfo storageVolumeInfo = clusterInfo.getStorageVolume(volumeInfo);
if (storageVolumeInfo == null) {
s_logger.info("Storage volume with WWN {} was not found in cluster {}", volumeWWN, clusterInfo.getName());
String volumeName = volumeInfo.getVolumeName();
storageVolumeInfo = clusterInfo.getStorageVolume(volumeInfo);
if (storageVolumeInfo != null) {
// The storage volume requested for an operation is
// already claimed. For now, we just log a warning so
// that stale VPLEX artifacts associated with this
// storage volume can be easily identified and purged.
s_logger.warn("The claimed storage volume {} has WWN {}", volumeName, volumeWWN);
}
continue;
}
volumeFound = true;
s_logger.info("Found storage volume {}", storageVolumeInfo.toString());
storageVolumeInfo.setClusterId(clusterInfo.getName());
storageVolumeInfoMap.put(volumeInfo, storageVolumeInfo);
break;
}
}
if (!volumeFound) {
throw VPlexApiException.exceptions.couldNotFindStorageVolumeMatchingWWNOrITL(volumeWWN, storageSystemNativeGuid);
}
}
return storageVolumeInfoMap;
}
/**
* Attempts to find the storage volume with the passed name.
*
* @param storageVolumeName The name of the storage volume.
*
* @return A VPlexStorageVolumeInfo representing the storage volume with the passed name or
* null.
*
* @throws VPlexApiException When an error occurs finding the storage volume.
*/
VPlexStorageVolumeInfo findStorageVolume(String storageVolumeName)
throws VPlexApiException {
VPlexStorageVolumeInfo storageVolumeInfo = null;
List<VPlexClusterInfo> clusterInfoList = getClusterInfoLite();
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
String clusterName = clusterInfo.getName();
s_logger.info("Find storage volume {} on cluster {}", storageVolumeName, clusterName);
List<VPlexStorageVolumeInfo> storageVolumeInfoList = getStorageVolumeInfoForCluster(clusterName, false);
for (VPlexStorageVolumeInfo clusterVolumeInfo : storageVolumeInfoList) {
if (clusterVolumeInfo.getName().equals(storageVolumeName)) {
storageVolumeInfo = clusterVolumeInfo;
storageVolumeInfo.setClusterId(clusterName);
break;
}
}
// We found the extent.
if (storageVolumeInfo != null) {
break;
}
}
return storageVolumeInfo;
}
/**
* Finds the extents created for the passed VPlex storage volumes.
*
* @param storageVolumeInfoList The storage volumes whose extents to find.
*
* @return A List of the VPlex extents
*
* @throws VPlexApiException When an error occurs finding the extents.
*/
List<VPlexExtentInfo> findExtents(List<VPlexStorageVolumeInfo> storageVolumeInfoList) throws VPlexApiException {
List<VPlexExtentInfo> extentInfoList = new ArrayList<VPlexExtentInfo>();
Iterator<VPlexStorageVolumeInfo> storageVolumeIter = storageVolumeInfoList.iterator();
while (storageVolumeIter.hasNext()) {
VPlexStorageVolumeInfo storageVolumeInfo = storageVolumeIter.next();
boolean extentFound = false;
int retryCount = 0;
while (++retryCount <= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
try {
String storageVolumeName = storageVolumeInfo.getName();
s_logger.info("Find extent for volume {}", storageVolumeName);
List<VPlexExtentInfo> clusterExtentInfoList = getExtentInfoForCluster(storageVolumeInfo.getClusterId());
for (VPlexExtentInfo extentInfo : clusterExtentInfoList) {
s_logger.debug("Extent Info: {}", extentInfo.toString());
StringBuilder nameBuilder = new StringBuilder();
nameBuilder.append(VPlexApiConstants.EXTENT_PREFIX);
nameBuilder.append(storageVolumeName);
nameBuilder.append(VPlexApiConstants.EXTENT_SUFFIX);
if (extentInfo.getName().equals(nameBuilder.toString())) {
s_logger.info("Found extent for volume {}", storageVolumeName);
extentFound = true;
extentInfo.setStorageVolumeInfo(storageVolumeInfo);
extentInfo.setClusterId(storageVolumeInfo.getClusterId());
extentInfoList.add(extentInfo);
break;
}
}
if (!extentFound) {
s_logger.warn("Extent not found on try {} of {}", retryCount,
VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES);
if (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw VPlexApiException.exceptions
.cantFindExtentForClaimedVolume(storageVolumeName);
}
} else {
break;
}
} catch (VPlexApiException vae) {
s_logger.error(String.format("Exception finding extent on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), vae);
if (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw vae;
}
} catch (Exception e) {
s_logger.error(String.format("Exception finding extent on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), e);
if (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw e;
}
}
}
}
return extentInfoList;
}
/**
* Attempts to find the extent with the passed name.
*
* @param extentName The name of the extent.
*
* @return A VPlexExtentInfo representing the extent with the passed name or
* null.
*
* @throws VPlexApiException When an error occurs finding the extent.
*/
VPlexExtentInfo findExtent(String extentName) throws VPlexApiException {
VPlexExtentInfo extentInfo = null;
List<VPlexClusterInfo> clusterInfoList = getClusterInfoLite();
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
String clusterName = clusterInfo.getName();
s_logger.info("Find extent {} on cluster {}", extentName, clusterName);
List<VPlexExtentInfo> extentInfoList = getExtentInfoForCluster(clusterName);
for (VPlexExtentInfo clusterExtentInfo : extentInfoList) {
if (clusterExtentInfo.getName().equals(extentName)) {
extentInfo = clusterExtentInfo;
extentInfo.setClusterId(clusterName);
break;
}
}
// We found the extent.
if (extentInfo != null) {
break;
}
}
return extentInfo;
}
/**
* Gets the extents on the cluster with the passed name.
*
* @param clusterName The name of the cluster.
*
* @return A list of VPlexExtentInfo instances for the extents found.
*
* @throws VPlexApiException When an error occurs getting the extents on the
* cluster.
*/
private List<VPlexExtentInfo> getExtentInfoForCluster(String clusterName)
throws VPlexApiException {
ClientResponse response = null;
try {
// Get the URI for the extent info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_EXTENTS.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Extents Request URI is {}", requestURI.toString());
response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.debug("Response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(String.format(
"Failed getting info for VPlex extents with status: %s",
response.getStatus()));
}
// Successful Response
List<VPlexExtentInfo> clusterExtentInfoList = VPlexApiUtils
.getChildrenFromResponse(uriBuilder.toString(), responseStr,
VPlexExtentInfo.class);
return clusterExtentInfoList;
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing extent information: %s", e.getMessage()), e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Find the local devices for the passed VPlex extents.
*
* @param extentInfoList The extents for which to find the local devices.
*
* @return A list of the local device info for the passed extents.
*
* @throws VPlexApiException When an error occurs finding the devices.
*/
List<VPlexDeviceInfo> findLocalDevices(List<VPlexExtentInfo> extentInfoList)
throws VPlexApiException {
List<VPlexDeviceInfo> deviceInfoList = new ArrayList<VPlexDeviceInfo>();
Iterator<VPlexExtentInfo> extentIter = extentInfoList.iterator();
while (extentIter.hasNext()) {
VPlexExtentInfo extentInfo = extentIter.next();
int retryCount = 0;
boolean deviceFound = false;
while (++retryCount <= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
try {
VPlexStorageVolumeInfo storageVolumeInfo = extentInfo.getStorageVolumeInfo();
String baseDeviceName = storageVolumeInfo.getName();
StringBuilder deviceNameBuilder = new StringBuilder();
deviceNameBuilder.append(VPlexApiConstants.DEVICE_PREFIX);
deviceNameBuilder.append(baseDeviceName);
s_logger.info("Find device with name {}", deviceNameBuilder.toString());
// Get the devices on the cluster for this extent.
List<VPlexDeviceInfo> clusterDeviceInfoList = getLocalDeviceInfoOnCluster(storageVolumeInfo.getClusterId());
for (VPlexDeviceInfo deviceInfo : clusterDeviceInfoList) {
s_logger.debug("Device Info: {}", deviceInfo.toString());
if (deviceInfo.getName().equals(deviceNameBuilder.toString())) {
s_logger.info("Found device for extent {}", extentInfo.getName());
deviceFound = true;
List<VPlexExtentInfo> deviceExtentInfoList = new ArrayList<VPlexExtentInfo>();
deviceExtentInfoList.add(extentInfo);
deviceInfo.setExtentInfo(deviceExtentInfoList);
deviceInfo.setCluster(storageVolumeInfo.getClusterId());
deviceInfoList.add(deviceInfo);
break;
}
}
if (!deviceFound) {
s_logger.warn("Local device not found on try {} of {}", retryCount,
VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES);
if (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw VPlexApiException.exceptions
.cantFindLocalDeviceForExtent(extentInfo.getName());
}
} else {
break;
}
} catch (VPlexApiException vae) {
s_logger.error(String.format("Exception finding local device on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), vae);
if (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw vae;
}
} catch (Exception e) {
s_logger.error(String.format("Exception finding local device on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), e);
if (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw e;
}
}
}
}
return deviceInfoList;
}
/**
* Attempts to find the local device with the passed name.
*
* @param deviceName The name of the local device.
*
* @return A VPlexDeviceInfo representing the local device with the passed
* name or null.
*
* @throws VPlexApiException When an error occurs finding the local device.
*/
VPlexDeviceInfo findLocalDevice(String deviceName) throws VPlexApiException {
VPlexDeviceInfo deviceInfo = null;
List<VPlexClusterInfo> clusterInfoList = getClusterInfoLite();
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
String clusterName = clusterInfo.getName();
s_logger.info("Find device {} on cluster {}", deviceName, clusterName);
List<VPlexDeviceInfo> deviceInfoList = getLocalDeviceInfoOnCluster(clusterName);
for (VPlexDeviceInfo clusterDeviceInfo : deviceInfoList) {
if (clusterDeviceInfo.getName().equals(deviceName)) {
deviceInfo = clusterDeviceInfo;
deviceInfo.setCluster(clusterName);
break;
}
}
// We found the device.
if (deviceInfo != null) {
break;
}
}
return deviceInfo;
}
/**
* Gets the devices on the cluster with the passed name.
*
* @param clusterName The name of the cluster.
*
* @return A list of VPlexDeviceInfo instances for the devices found.
*
* @throws VPlexApiException When an error occurs getting the devices on the
* cluster.
*/
private List<VPlexDeviceInfo> getLocalDeviceInfoOnCluster(String clusterName)
throws VPlexApiException {
ClientResponse response = null;
try {
// Get the URI for the device info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_DEVICES.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Devices Request URI is {}", requestURI.toString());
response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.debug("Response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(String.format(
"Failed getting info for VPlex devices with status: %s",
response.getStatus()));
}
// Successful Response
List<VPlexDeviceInfo> clusterDeviceInfoList = VPlexApiUtils
.getChildrenFromResponse(uriBuilder.toString(), responseStr,
VPlexDeviceInfo.class);
return clusterDeviceInfoList;
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing device information: %s", e.getMessage()), e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Finds the distributed device with the passed name.
*
* @param deviceName The name of the distributed device to find.
*
* @return A reference to the distributed device info or null if not found.
*
* @throws VPlexApiException When an error occurs finding the device.
*/
VPlexDistributedDeviceInfo findDistributedDevice(String deviceName)
throws VPlexApiException {
return findDistributedDevice(deviceName, false);
}
/**
* Finds the distributed device with the passed name.
*
* @param deviceName The name of the distributed device to find.
* @param retry Indicates retry should occur if the first attempt to find
* the distributed device fails.
*
* @return A reference to the distributed device info or null if not found.
*
* @throws VPlexApiException When an error occurs finding the device.
*/
VPlexDistributedDeviceInfo findDistributedDevice(String deviceName, boolean retry)
throws VPlexApiException {
s_logger.info("Find distributed device with name {}", deviceName);
int retryCount = 0;
VPlexDistributedDeviceInfo distributedDeviceInfo = null;
while (++retryCount <= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
try {
List<VPlexDistributedDeviceInfo> deviceInfoList = getDistributedDeviceInfo();
for (VPlexDistributedDeviceInfo deviceInfo : deviceInfoList) {
s_logger.debug("Distributed Device Info: {}", deviceInfo.toString());
if (deviceInfo.getName().equals(deviceName)) {
s_logger.info("Found distributed device {}", deviceName);
distributedDeviceInfo = deviceInfo;
break;
}
}
if ((distributedDeviceInfo != null) || (!retry) ||
(retryCount >= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
break;
} else {
s_logger.warn("Distributed device not found on try {} of {}", retryCount,
VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
}
} catch (VPlexApiException vae) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error(String.format("Exception finding distributed device on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), vae);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw vae;
}
} catch (Exception e) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error(String.format("Exception finding distributed device on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), e);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw e;
}
}
}
return distributedDeviceInfo;
}
/**
* Find the virtual volume containing the passed name.
*
* @param volumeName The name of the virtual volume.
* @param fetchAtts true to fetch the virtual volume attributes.
*
* @return A reference to the virtual volume info.
*
* @throws VPlexApiException When an error occurs finding the virtual
* volume.
*/
VPlexVirtualVolumeInfo findVirtualVolume(String volumeName, boolean fetchAtts)
throws VPlexApiException {
if (volumeName == null) {
throw VPlexApiException.exceptions.cantFindRequestedVolumeNull();
}
// Find the virtual volume.
VPlexVirtualVolumeInfo virtualVolumeInfo = null;
List<VPlexClusterInfo> clusterInfoList = getClusterInfoLite();
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
virtualVolumeInfo = findVirtualVolume(clusterInfo.getName(),
volumeName, fetchAtts);
if (virtualVolumeInfo != null) {
break;
}
}
// Throw an exception if we can't find the volume.
if (virtualVolumeInfo == null) {
throw VPlexApiException.exceptions.cantFindRequestedVolume(volumeName);
}
return virtualVolumeInfo;
}
/**
* Find the virtual volume containing the passed name.
*
* @param clusterId The id of the cluster on which to find the virtual volume.
* @param volumeName The name of the virtual volume.
* @param fetchAtts true to fetch the virtual volume attributes.
*
* @return A reference to the virtual volume info or null if not found.
*
* @throws VPlexApiException When an error occurs finding the virtual
* volume.
*/
VPlexVirtualVolumeInfo findVirtualVolume(String clusterId, String volumeName,
Boolean fetchAtts) throws VPlexApiException {
return findVirtualVolume(clusterId, volumeName, fetchAtts, false);
}
/**
* Find the virtual volume containing the passed name.
*
* @param clusterId The id of the cluster on which to find the virtual volume.
* @param volumeName The name of the virtual volume.
* @param fetchAtts true to fetch the virtual volume attributes.
* @param retry Indicates retry should occur if the first attempt to find
* the virtual volume fails.
*
* @return A reference to the virtual volume info or null if not found.
*
* @throws VPlexApiException When an error occurs finding the virtual
* volume.
*/
VPlexVirtualVolumeInfo findVirtualVolume(String clusterId, String volumeName,
Boolean fetchAtts, boolean retry) throws VPlexApiException {
if (volumeName == null) {
throw VPlexApiException.exceptions.cantFindRequestedVolumeNull();
}
s_logger.info("Find virtual volume with name {}", volumeName);
int retryCount = 0;
while (++retryCount <= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
try {
List<VPlexVirtualVolumeInfo> clusterVolumeInfoList = getVirtualVolumesForCluster(clusterId);
for (VPlexVirtualVolumeInfo volumeInfo : clusterVolumeInfoList) {
s_logger.debug("Virtual volume Info: {}", volumeInfo.toString());
if (volumeInfo.getName().equals(volumeName)) {
s_logger.info("Found virtual volume {}", volumeInfo.getName());
return volumeInfo;
}
}
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.warn("Virtual volume not found on try {} of {}", retryCount,
VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
break;
}
} catch (VPlexApiException vae) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error(String.format("Exception finding virtual volume on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), vae);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw vae;
}
} catch (Exception e) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error(String.format("Exception finding virtual volume on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), e);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw e;
}
}
}
return null;
}
/**
* Find the virtual volume(s) containing the passed name in the
* virtualVolumeInfos list.
*
* @param clusterInfoList A list of VPlexClusterInfo specifying the info for the VPlex
* clusters.
* @param virtualVolumeInfos List of virtual volumes to find.
* @param fetchAtts true to fetch the virtual volume attributes.
* @param retry Indicates retry should occur if the first attempt to find
* the virtual volume fails.
*
* @return A map of virtual volume name to the virtual volume info.
*
* @throws VPlexApiException When an error occurs finding the virtual
* volume.
*/
Map<String, VPlexVirtualVolumeInfo> findVirtualVolumes(List<VPlexClusterInfo> clusterInfoList,
List<VPlexVirtualVolumeInfo> virtualVolumeInfos,
boolean fetchAtts, boolean retry) throws VPlexApiException {
if (virtualVolumeInfos == null) {
throw VPlexApiException.exceptions.cantFindRequestedVolumeNull();
}
StringBuffer volumeNameStrBuf = new StringBuffer();
// Make a map of virtual volume name to VPlexVirtualVolumeInfo
Map<String, VPlexVirtualVolumeInfo> virtualVolumesToFind = new HashMap<String, VPlexVirtualVolumeInfo>();
for (VPlexVirtualVolumeInfo virtualVolumeInfo : virtualVolumeInfos) {
volumeNameStrBuf.append(virtualVolumeInfo.getName()).append(" ");
virtualVolumesToFind.put(virtualVolumeInfo.getName(), virtualVolumeInfo);
}
s_logger.info("Find virtual volume(s) containing {}", volumeNameStrBuf.toString());
// Make a map of virtual volume name to VPlexVirtualVolumeInfo for the virtual volume found on VPLEX.
Map<String, VPlexVirtualVolumeInfo> foundVirtualVolumes = new HashMap<String, VPlexVirtualVolumeInfo>();
int retryCount = 0;
while (++retryCount <= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
try {
// Make a map of VPLEX cluster to virtual volumes found on that cluster.
Map<String, List<VPlexVirtualVolumeInfo>> clusterToVirtualVolumeMap = new HashMap<String, List<VPlexVirtualVolumeInfo>>();
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
List<VPlexVirtualVolumeInfo> clusterVolumeInfoList = getVirtualVolumesForCluster(clusterInfo.getName());
clusterToVirtualVolumeMap.put(clusterInfo.getName(), clusterVolumeInfoList);
}
List<VPlexVirtualVolumeInfo> virtualVolumeToFindList = new ArrayList<VPlexVirtualVolumeInfo>();
for (Map.Entry<String, VPlexVirtualVolumeInfo> entry : virtualVolumesToFind.entrySet()) {
virtualVolumeToFindList.add(entry.getValue());
}
for (VPlexVirtualVolumeInfo virtualVolumeInfo : virtualVolumeToFindList) {
List<VPlexVirtualVolumeInfo> clusterVolumeInfoList = clusterToVirtualVolumeMap
.get(virtualVolumeInfo.getClusters().get(0));
for (VPlexVirtualVolumeInfo volumeInfo : clusterVolumeInfoList) {
s_logger.debug("Virtual volume Info: {}", volumeInfo.toString());
if (volumeInfo.getName().equals(virtualVolumeInfo.getName())) {
s_logger.info("Found virtual volume {}", volumeInfo.getName());
foundVirtualVolumes.put(virtualVolumeInfo.getName(), volumeInfo);
// Remove the found virtual volume from the virtualVolumesToFind Map
virtualVolumesToFind.remove(virtualVolumeInfo.getName());
}
}
}
if (!foundVirtualVolumes.isEmpty() && foundVirtualVolumes.size() == virtualVolumeInfos.size()) {
return foundVirtualVolumes;
}
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.warn(String.format("Virtual volumes %s not found on try %d of %d",
geAllVolumeNamesFromMap(virtualVolumesToFind),
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES));
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
break;
}
} catch (VPlexApiException vae) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error(String.format("Exception finding virtual volumes on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), vae);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
if (!foundVirtualVolumes.isEmpty()) {
return foundVirtualVolumes;
} else {
throw vae;
}
}
} catch (Exception e) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error(String.format("Exception finding virtual volumes on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), e);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
if (!foundVirtualVolumes.isEmpty()) {
return foundVirtualVolumes;
} else {
throw e;
}
}
}
}
return null;
}
/**
* This method returns all volume names from the map.
*
* @param virtualVolumesToFind Map of Volume name to volume info
* @return returns all volume names from the Map
*/
private String geAllVolumeNamesFromMap(Map<String, VPlexVirtualVolumeInfo> virtualVolumesToFind) {
StringBuffer volumesBuffer = new StringBuffer();
if (!virtualVolumesToFind.isEmpty()) {
Set<String> volumeNames = virtualVolumesToFind.keySet();
for (String volumeName : volumeNames) {
volumesBuffer.append(volumeName).append(" ");
}
}
return volumesBuffer.toString();
}
/**
* Get the storage system info for the cluster with the passed name.
*
* @param clusterName The name of the cluster.
*
* @return A list of VPlexStorageSystemInfo specifying the storage system
* info for the cluster with the passed name.
*
* @throws VPlexApiException If a VPlex request returns a failed status or
* an error occurs processing the response.
*/
private List<VPlexStorageSystemInfo> getStorageSystemInfoForCluster(String clusterName)
throws VPlexApiException {
// Get the URI for the storage system info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_STORAGE_SYSTEMS.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Storage Systems Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(String.format(
"Failed getting storage system info for cluster %s with status: %s",
clusterName, status));
}
// Successful Response
try {
List<VPlexStorageSystemInfo> storageSystemInfoList = VPlexApiUtils.getChildrenFromResponse(
uriBuilder.toString(), responseStr, VPlexStorageSystemInfo.class);
for (VPlexStorageSystemInfo storageSystemInfo : storageSystemInfoList) {
storageSystemInfo.buildUniqueId();
storageSystemInfo.setClusterId(clusterName);
}
return storageSystemInfoList;
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing storage system information: %s", e.getMessage()), e);
}
}
/**
* Get the storage volume info for the cluster with the passed name.
*
* @param clusterName The name of the cluster.
*
* @return A list of VPlexStorageVolumeInfo specifying the storage volume
* info for the cluster with the passed name.
* @param getStorageVolumeAtts true to get the storage volume attributes, false otherwise.
*
* @throws VPlexApiException If a VPlex request returns a failed status or
* an error occurs processing the response.
*/
private List<VPlexStorageVolumeInfo> getStorageVolumeInfoForCluster(String clusterName, boolean getStorageVolumeAtts)
throws VPlexApiException {
// Get the URI for the storage volume info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
String responseJsonFormat = null;
if (getStorageVolumeAtts) {
uriBuilder.append(VPlexApiConstants.URI_STORAGE_VOLUMES_DETAILS.toString());
responseJsonFormat = VPlexApiConstants.ACCEPT_JSON_FORMAT_1;
} else {
uriBuilder.append(VPlexApiConstants.URI_STORAGE_VOLUMES.toString());
responseJsonFormat = VPlexApiConstants.ACCEPT_JSON_FORMAT_0;
}
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Storage Volumes Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI, responseJsonFormat);
String responseStr = response.getEntity(String.class);
s_logger.debug("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.failedGettingStorageVolumeInfo(clusterName, String.valueOf(status));
}
// Successful Response
try {
if (getStorageVolumeAtts) {
return VPlexApiUtils.getResourcesFromResponseContext(
uriBuilder.toString(), responseStr, VPlexStorageVolumeInfo.class);
} else {
return VPlexApiUtils.getChildrenFromResponse(
uriBuilder.toString(), responseStr, VPlexStorageVolumeInfo.class);
}
} catch (Exception e) {
throw VPlexApiException.exceptions.failedProcessingStorageVolumeResponse(e.getMessage(), e);
}
}
/**
* Get the system volume info for the cluster with the passed name.
*
* @param clusterName The cluster name.
*
* @return A list of VPlexSystemVolumeInfo specifying the system volume
* information for the cluster.
*
* @throws VPlexApiException When an error occurs getting the system volume
* information for the cluster.
*/
private List<VPlexSystemVolumeInfo> getSystemVolumeInfoForCluster(String clusterName)
throws VPlexApiException {
// Get the URI for the logging volume info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_SYSTEM_VOLUMES.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Logging Volumes Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.debug("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(String.format(
"Failed getting logging volume info for cluster %s with status: %s",
clusterName, status));
}
// Successful Response
try {
List<VPlexSystemVolumeInfo> systemVolumeInfoList = VPlexApiUtils.getChildrenFromResponse(
uriBuilder.toString(), responseStr, VPlexSystemVolumeInfo.class);
for (VPlexSystemVolumeInfo systemVolumeInfo : systemVolumeInfoList) {
updateSystemVolumeInfo(clusterName, systemVolumeInfo);
}
return systemVolumeInfoList;
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing logging volume information: %s", e.getMessage()), e);
}
}
/**
* Updates the attribute info for the passed system volume.
*
* @param clusterName The name cluster containing the passed system volume.
* @param systemVolumeInfo The system volume to update.
*
* @throws VPlexApiException When an error occurs updating the system volume
* attribute info.
*/
private void updateSystemVolumeInfo(String clusterName,
VPlexSystemVolumeInfo systemVolumeInfo) throws VPlexApiException {
// Get the URI for the system volume info request and make the request.
String systemVolumeName = systemVolumeInfo.getName();
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_SYSTEM_VOLUMES.toString());
uriBuilder.append(systemVolumeName);
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.debug("System Volume Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.debug("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(
String.format("Failed getting info for system volume %s in cluster %s with status: %s",
systemVolumeName, clusterName, status));
}
// Now parse this response to populate the system volume details in the passed
// system volume info.
try {
VPlexApiUtils.setAttributeValues(responseStr, systemVolumeInfo);
s_logger.debug("Updated System Volume Info {}", systemVolumeInfo.toString());
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing system volume information: %s", e.getMessage()), e);
}
}
/**
* Gets the information for the VPlex engines.
*
* @return A list of VPlexEngineInfo specifying the info for the VPlex
* engines.
*
* @throws VPlexApiException If a VPlex request returns a failed status or
* an error occurs processing the response.
*/
private List<VPlexEngineInfo> getEngineInfo() throws VPlexApiException {
// Get the URI for the engine info request and make the request.
URI requestURI = _vplexApiClient.getBaseURI().resolve(VPlexApiConstants.URI_ENGINES);
s_logger.info("Engines Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(String.format(
"Failed getting info for VPlex engines with status: %s",
status));
}
// Successful Response
try {
List<VPlexEngineInfo> engineInfoList = VPlexApiUtils.getChildrenFromResponse(
VPlexApiConstants.URI_ENGINES.toString(), responseStr,
VPlexEngineInfo.class);
for (VPlexEngineInfo engineInfo : engineInfoList) {
s_logger.info("Engine Info: {}", engineInfo.toString());
engineInfo
.setDirectorInfo(getDirectorInfoForEngine(engineInfo.getName()));
}
return engineInfoList;
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing engine information: %s", e.getMessage()), e);
}
}
/**
* Gets the information for the VPlex directors for the passed engine.
*
* @param engineName The name of the VPlex engine.
*
* @return A list of VPlexDirectorInfo specifying the info for the VPlex
* directors.
*
* @throws VPlexApiException If a VPlex request returns a failed status or
* an error occurs processing the response.
*/
private List<VPlexDirectorInfo> getDirectorInfoForEngine(String engineName)
throws VPlexApiException {
// Get the URI for the director info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_ENGINES.toString());
uriBuilder.append(engineName);
uriBuilder.append(VPlexApiConstants.URI_DIRECTORS.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Directors Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(String.format(
"Failed getting director info for VPlex engine %s with status: %s",
engineName, status));
}
// Successful Response
try {
List<VPlexDirectorInfo> directorInfoList = VPlexApiUtils.getChildrenFromResponse(
uriBuilder.toString(), responseStr, VPlexDirectorInfo.class);
for (VPlexDirectorInfo directorInfo : directorInfoList) {
updateDirectorInfo(engineName, directorInfo);
s_logger.info("Director Info: {}", directorInfo.toString());
directorInfo.setPortInfo(getPortInfoForDirector(engineName,
directorInfo));
}
return directorInfoList;
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing director information: %s", e.getMessage()), e);
}
}
/**
* Updates the attribute info for the passed VPlex director.
*
* @param engineName The name engine containing the passed VPlex director.
* @param directorInfo The VPlex director to update.
*
* @throws VPlexApiException When an error occurs updating the director
* attribute info.
*/
private void updateDirectorInfo(String engineName, VPlexDirectorInfo directorInfo)
throws VPlexApiException {
// Get the URI for the director info request and make the request.
String directorName = directorInfo.getName();
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_ENGINES.toString());
uriBuilder.append(engineName);
uriBuilder.append(VPlexApiConstants.URI_DIRECTORS.toString());
uriBuilder.append(directorName);
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Director Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(
String.format("Failed getting info for director %s in engine %s with status: %s",
directorName, engineName, status));
}
// Now parse this response to populate the director details in the passed
// director info.
try {
VPlexApiUtils.setAttributeValues(responseStr, directorInfo);
s_logger.info("Updated Director Info {}", directorInfo.toString());
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing director information: %s", e.getMessage()), e);
}
}
/**
* Gets the information for the VPlex ports for the passed director.
*
* @param directorInfo The VPlex director info.
*
* @return A list of VPlexPortInfo specifying the info for the VPlex ports.
*
* @throws VPlexApiException If a VPlex request returns a failed status or
* an error occurs processing the response.
*/
private List<VPlexPortInfo> getPortInfoForDirector(String engineName,
VPlexDirectorInfo directorInfo) throws VPlexApiException {
// Get the URI for the port info request and make the request.
String directorName = directorInfo.getName();
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_ENGINES.toString());
uriBuilder.append(engineName);
uriBuilder.append(VPlexApiConstants.URI_DIRECTORS.toString());
uriBuilder.append(directorName);
uriBuilder.append(VPlexApiConstants.URI_DIRECTOR_PORTS.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Director Ports Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(
String.format("Failed getting port info for VPlex director %s in engine %s with status: %s",
directorName, engineName, status));
}
// Successful Response
try {
List<VPlexPortInfo> portInfoList = VPlexApiUtils.getChildrenFromResponse(uriBuilder.toString(),
responseStr, VPlexPortInfo.class);
for (VPlexPortInfo portInfo : portInfoList) {
s_logger.info("Port Info: {}", portInfo.toString());
portInfo.setDirectorInfo(directorInfo);
updatePortInfo(engineName, directorName, portInfo);
}
return portInfoList;
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing director port information: %s", e.getMessage()), e);
}
}
/**
* Updates the attribute info for the passed VPlex port.
*
* @param engineName The name engine containing the passed VPlex director.
* @param directorName The name of the director containing the passed port.
* @param portInfo The VPlex port to update.
*
* @throws VPlexApiException When an error occurs updating the port
* attribute info.
*/
private void updatePortInfo(String engineName, String directorName,
VPlexPortInfo portInfo) throws VPlexApiException {
// Get the URI for the port info request and make the request.
String portName = portInfo.getName();
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_ENGINES.toString());
uriBuilder.append(engineName);
uriBuilder.append(VPlexApiConstants.URI_DIRECTORS.toString());
uriBuilder.append(directorName);
uriBuilder.append(VPlexApiConstants.URI_DIRECTOR_PORTS.toString());
uriBuilder.append(portName);
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Port Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(
String.format("Failed getting info for port %s on VPlex director %s in engine %s with status: %s",
portName, directorName, engineName, status));
}
// Now parse this response to populate the port details in the passed
// port info.
try {
VPlexApiUtils.setAttributeValues(responseStr, portInfo);
s_logger.info("Updated Port Info {}", portInfo.toString());
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing port information: %s", e.getMessage()), e);
}
}
/**
* Returns a cached List of VPlexInitiatorInfo objects for the cluster.
*
* @param clusterName indicates which VPlex cluster to check
* @param fetchFromArray {@link Boolean} if true passed, Fetch the initiator information from array and updates the local cache
* @return a List of VPlexInitiatorInfos for the given cluster
*/
synchronized List<VPlexInitiatorInfo> getInitiatorInfoForCluster(String clusterName, Boolean fetchFromArray) {
if (fetchFromArray || CollectionUtils.isEmpty(_vplexClusterInitiatorInfoCache.get(clusterName))) {
long start = System.currentTimeMillis();
s_logger.info("refreshing VPlexInitiatorInfo cache for cluster " + clusterName);
// Get the URI for the initiator info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_INITIATORS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Initiators Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI, VPlexApiConstants.ACCEPT_JSON_FORMAT_1);
s_logger.info("TIMER: fetching all initiators for cluster {} took {}ms", clusterName, System.currentTimeMillis() - start);
String responseStr = response.getEntity(String.class);
int status = response.getStatus();
response.close();
if (status == VPlexApiConstants.SUCCESS_STATUS) {
try {
List<VPlexInitiatorInfo> clusterInitiatorInfo = VPlexApiUtils.getResourcesFromResponseContext(
uriBuilder.toString(), responseStr, VPlexInitiatorInfo.class);
_vplexClusterInitiatorInfoCache.put(clusterName, clusterInitiatorInfo);
} catch (Exception e) {
throw VPlexApiException.exceptions.errorProcessingInitiatorInformation(e.getLocalizedMessage());
}
} else if (status == VPlexApiConstants.NOT_FOUND_STATUS) {
// return an empty list rather than an error
s_logger.info("VPLEX returned a 404 Not Found for this context, returning an empty list instead.");
return new ArrayList<VPlexInitiatorInfo>();
} else {
throw VPlexApiException.exceptions
.failedGettingInitiatorInfoForCluster(clusterName, String.valueOf(status));
}
s_logger.info("TIMER: refreshing VPlexInitiatorInfo cache took {}ms", System.currentTimeMillis() - start);
}
// return a copy
return new ArrayList<VPlexInitiatorInfo>(_vplexClusterInitiatorInfoCache.get(clusterName));
}
/**
* Updates the attribute data for the passed initiator on the passed
* cluster.
*
* @param clusterName The name of the cluster.
* @param initiatorInfo The initiator whose attributes are to be set.
*
* @throws VPlexApiException When an error occurs updating the initiator
* attributes.
*/
private void updateInitiatorInfo(String clusterName, VPlexInitiatorInfo initiatorInfo)
throws VPlexApiException {
// Get the URI for the initiator info request and make the request.
String initiatorName = initiatorInfo.getName();
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_INITIATORS.toString());
uriBuilder.append(initiatorName);
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Initiator Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.debug("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(
String.format("Failed getting info for initiator %s in cluster %s with status: %s",
initiatorName, clusterName, status));
}
// Now parse this response to populate the initiator details in the passed
// initiator info.
try {
VPlexApiUtils.setAttributeValues(responseStr, initiatorInfo);
s_logger.info("Updated Initiator Info {}", initiatorInfo.toString());
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing initiator information: %s", e.getMessage()), e);
}
}
/**
* Executes an initiator discovery on the passed cluster.
*
* @param clusterInfo The cluster on which initiators are to be discovered.
*
* @throws VPlexApiException When an error occurs executing the discovery.
*/
void discoverInitiatorsOnCluster(VPlexClusterInfo clusterInfo)
throws VPlexApiException {
ClientResponse response = null;
try {
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_INITIATOR_DISCOVERY);
s_logger.info("Initiator discovery URI is {}", requestURI.toString());
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_C, clusterInfo.getPath());
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false);
s_logger.info("Initiator discovery POST data is {}",
postDataObject.toString());
response = _vplexApiClient.post(requestURI,
postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.debug("Initiator discovery response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
_vplexApiClient.waitForCompletion(response);
} else {
throw new VPlexApiException(String.format(
"Request initiator discovery failed with Status: %s",
response.getStatus()));
}
}
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Exception during initiator discovery: %s", e.getMessage()), e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Gets information for the target FE ports on the cluster with the passed
* name.
*
* @param clusterName The name of the cluster.
*
* @return A list of VPlexTargetInfo instances specifying the target
* information.
*
* @throws VPlexApiException When an error occurs getting the target
* information for the cluster.
*/
List<VPlexTargetInfo> getTargetInfoForCluster(String clusterName)
throws VPlexApiException {
// Get the URI for the initiator info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_TARGETS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Targets Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI, VPlexApiConstants.ACCEPT_JSON_FORMAT_1);
String responseStr = response.getEntity(String.class);
s_logger.debug("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status == VPlexApiConstants.SUCCESS_STATUS) {
try {
return VPlexApiUtils.getResourcesFromResponseContext(
uriBuilder.toString(), responseStr, VPlexTargetInfo.class);
} catch (Exception e) {
throw VPlexApiException.exceptions.errorProcessingTargetPortInformation(String.valueOf(status));
}
} else if (status == VPlexApiConstants.NOT_FOUND_STATUS) {
// return an empty list rather than an error
s_logger.info("VPLEX returned a 404 Not Found for this context, returning an empty list instead.");
return new ArrayList<VPlexTargetInfo>();
} else {
throw VPlexApiException.exceptions.failedGettingTargetPortInfo(String.valueOf(status));
}
}
/**
* Finds the storage view with the passed name on the cluster with the
* passed cluster name.
*
* @param viewName The name of the storage view to be found.
* @param clusterName The names of the cluster on which to find the storage
* view.
*
* @return A VPlexStorageViewInfo instance specifying the storage view
* information or null when not found.
*
* @throws VPlexApiException When an error occurs finding the storage view.
*/
VPlexStorageViewInfo findStorageViewOnCluster(String viewName, String clusterName, Boolean includeDetails)
throws VPlexApiException {
return findStorageViewOnCluster(viewName, clusterName, includeDetails, false);
}
/**
* Finds the storage view with the passed name on the cluster with the
* passed cluster name.
*
* @param viewName The name of the storage view to be found.
* @param clusterName The names of the cluster on which to find the storage
* view.
* @param includeDetails true to fetch the storage view attributes.
* @param retry Indicates retry should occur if the first attempt to find
* the storage view fails.
*
* @return A VPlexStorageViewInfo instance specifying the storage view
* information or null when not found.
*
* @throws VPlexApiException When an error occurs finding the storage view.
*/
VPlexStorageViewInfo findStorageViewOnCluster(String viewName, String clusterName,
Boolean includeDetails, boolean retry) throws VPlexApiException {
// Get the URI for the storage view info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_STORAGE_VIEWS.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Storage views request URI is {}", requestURI.toString());
int retryCount = 0;
VPlexStorageViewInfo storageViewInfo = null;
while (++retryCount <= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
try {
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.getStorageViewsFailed(String.format(
"Failed getting storage view info for cluster %s with status: %s",
clusterName, status));
}
// Successful Response
List<VPlexStorageViewInfo> storageViewInfoList = VPlexApiUtils
.getChildrenFromResponse(uriBuilder.toString(), responseStr,
VPlexStorageViewInfo.class);
storageViewInfo = null;
for (VPlexStorageViewInfo clusterStorageViewInfo : storageViewInfoList) {
s_logger.debug("Storage View Info: {}", clusterStorageViewInfo.toString());
if (clusterStorageViewInfo.getName().equals(viewName)) {
storageViewInfo = clusterStorageViewInfo;
storageViewInfo.setClusterId(clusterName);
// if true, the StorageViewInfo objects will include extended details
// that are not needed in most cases, e.g. initiators, ports, and virtual volumes
if (includeDetails) {
updateStorageViewInfo(storageViewInfo);
}
break;
}
}
if ((storageViewInfo != null) || (!retry)
|| (retryCount >= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
break;
} else {
s_logger.warn("Storage view not found on try {} of {}", retryCount,
VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
}
} catch (VPlexApiException vae) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error("Exception finding storage view on try {} of {}", retryCount,
VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw vae;
}
} catch (Exception e) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error("Exception finding storage view on try {} of {}", retryCount,
VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw VPlexApiException.exceptions
.getStorageViewsFailed(String.format(
"Exception getting storage view: %s", e.getMessage()));
}
}
}
return storageViewInfo;
}
/**
* Updates a VPlexStorageViewInfo object with detailed attributes
* that are not needed in most situations, e.g. initiators, ports,
* and virtual volumes.
*
* @param storageViewInfo
*/
void updateStorageViewInfo(VPlexStorageViewInfo storageViewInfo) {
// Get the URI for the storage view info request and make the request.
String storageViewName = storageViewInfo.getName();
String clusterName = storageViewInfo.getClusterId();
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(storageViewInfo.getClusterId());
uriBuilder.append(VPlexApiConstants.URI_STORAGE_VIEWS.toString());
uriBuilder.append(storageViewName);
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Storage View Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.getStorageViewsFailed(String.format("Failed getting info for storage "
+ "view %s in cluster %s with status: %s", storageViewName, clusterName,
status));
}
// Now parse this response to populate the storage view details in the
// passed storage view info.
try {
VPlexApiUtils.setAttributeValues(responseStr, storageViewInfo);
updateStorageViewInitiatorPWWN(storageViewInfo);
} catch (Exception e) {
throw VPlexApiException.exceptions.getStorageViewsFailed(String.format("Failed getting info for storage "
+ "view %s in cluster %s with status: %s", storageViewName, clusterName,
status));
}
}
/**
* Updates a VPlexStorageViewInfo object with the initiators PWWN
* based on the initiators.
*
* @param storageViewInfo The reference to VPlexStorageViewInfo
*/
void updateStorageViewInitiatorPWWN(VPlexStorageViewInfo storageViewInfo) {
List<String> initiators = storageViewInfo.getInitiators();
List<VPlexInitiatorInfo> initiatorsInfoList = new ArrayList<VPlexInitiatorInfo>();
for (String initiator : initiators) {
VPlexInitiatorInfo initiatorInfo = new VPlexInitiatorInfo();
initiatorInfo.setName(initiator);
updateInitiatorInfo(storageViewInfo.getClusterId(), initiatorInfo);
initiatorsInfoList.add(initiatorInfo);
}
List<String> initiatorPWWNs = new ArrayList<String>();
for (VPlexInitiatorInfo initiatorInfo : initiatorsInfoList) {
String pwwn = initiatorInfo.getPortWwn();
if (pwwn.startsWith(VPlexApiConstants.WWN_PREFIX)) {
pwwn = pwwn.substring(2);
}
pwwn = pwwn.toUpperCase();
initiatorPWWNs.add(pwwn);
}
storageViewInfo.setInitiatorPwwns(initiatorPWWNs);
s_logger.info("Updated Storage View Info {}", storageViewInfo.toString());
}
/**
* Returns a Set of storage view names for a given initiator name.
*
* @param clusterName the VPLEX cluster to look in
* @param initiatorName the initiator name
* @return a Set of storage view names
*/
private Set<String> findStorageViewNamesForInitiator(String clusterName,
String initiatorName) {
Set<String> viewNames = new HashSet<String>();
ClientResponse response = null;
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_FIND_STORAGE_VIEW);
s_logger.info("Find storage view URI is {}", requestURI.toString());
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_C, clusterName);
argsMap.put(VPlexApiConstants.ARG_DASH_I, initiatorName);
JSONObject postDataObject = VPlexApiUtils
.createPostData(argsMap, false);
s_logger.info("Find storage view POST data is {}",
postDataObject.toString());
response = _vplexApiClient.post(requestURI, postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Find storage view response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Get storage views for initaitor is completing asynchronously");
responseStr = _vplexApiClient.waitForCompletion(response);
s_logger.info("Task Response is {}", responseStr);
} else {
throw VPlexApiException.exceptions.getStorageViewsFailed(String
.format("Failed getting storage views: %s", status));
}
}
try {
String customData = VPlexApiUtils
.getCustomDataFromResponse(responseStr);
s_logger.info("Custom data from find storage view is {}",
customData);
// custom-data can look something like this:
// "Views including inititator *1000*:\nView V1_cluster123_host1hostcom_001.\n"
// so we're splitting on line breaks and taking the content of lines starting
// with "View " (also removing any trailing periods).
String[] lines = customData.split("\n");
for (int i = 1; i < lines.length; i++) {
String line = lines[i].replaceAll("^View ", "").replaceAll(
"\\.$", "");
viewNames.add(line);
}
} catch (Exception e) {
throw VPlexApiException.exceptions.getStorageViewsFailed(String
.format("Error processing storage views: %s",
e.getMessage()));
}
return viewNames;
}
/**
* Returns a list of VPlexStorageViewInfo objects representing
* storage views that contain the given initiator names.
*
* @param clusterName the VPLEX cluster to look in
* @param initiatorNames the initiator names to look for
* @return a list of VPlexStorageViewInfo objects
*/
public List<VPlexStorageViewInfo> getStorageViewsContainingInitiators(
String clusterName, List<String> initiatorNames) {
Set<String> viewNames = new HashSet<String>();
for (String initiatorName : initiatorNames) {
viewNames.addAll(findStorageViewNamesForInitiator(clusterName,
initiatorName));
}
List<VPlexStorageViewInfo> detailedStorageViews = new ArrayList<VPlexStorageViewInfo>();
for (String viewName : viewNames) {
VPlexStorageViewInfo svDetailed = findStorageViewOnCluster(
viewName, clusterName, true);
if (svDetailed != null) {
detailedStorageViews.add(svDetailed);
} else {
s_logger.warn("could not find details for storage view {} on cluster {}",
viewName, clusterName);
}
}
return detailedStorageViews;
}
/**
* Gets all the detailed Storage View infos for the entire VPLEX device.
*
* @param includeInitiatorDetails include initiator details (takes longer)
* @return list of all Storage View infos for a given VPLEX instance
* @throws VPlexApiException
*/
List<VPlexStorageViewInfo> getStorageViews(boolean includeInitiatorDetails) throws VPlexApiException {
return getStorageViewsForCluster(VPlexApiConstants.WILDCARD.toString(), includeInitiatorDetails);
}
/**
* Gets all the detailed Storage View infos for the give VPLEX cluster.
*
* @param clusterName name of the VPLEX cluster to look at, or you can send
* a wildcard (*) to get info from both clusters.
* @param includeInitiatorDetails include initiator details (takes longer)
* @return list of all Storage View infos for a given VPLEX instance
* @throws VPlexApiException
*/
List<VPlexStorageViewInfo> getStorageViewsForCluster(String clusterName, boolean includeInitiatorDetails) throws VPlexApiException {
s_logger.info("Getting all storage view information from VPLEX at " + _vplexApiClient.getBaseURI().toString());
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_STORAGE_VIEWS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Storage views request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI, VPlexApiConstants.ACCEPT_JSON_FORMAT_1);
String responseStr = response.getEntity(String.class);
int status = response.getStatus();
response.close();
if (status == VPlexApiConstants.SUCCESS_STATUS) {
try {
List<VPlexStorageViewInfo> storageViews = VPlexApiUtils
.getResourcesFromResponseContext(uriBuilder.toString(), responseStr,
VPlexStorageViewInfo.class);
if (includeInitiatorDetails) {
Map<String, String> initInfoMap = getInitiatorNameToWwnMap(clusterName, true);
for (VPlexStorageViewInfo sv : storageViews) {
// update storage views with wwpn info
for (String initName : sv.getInitiators()) {
String initWwn = initInfoMap.get(initName);
sv.getInitiatorPwwns().add(initWwn);
}
sv.refreshMaps();
}
}
return storageViews;
} catch (Exception e) {
throw VPlexApiException.exceptions.errorProcessingStorageViewInformation(e.getLocalizedMessage());
}
} else if (status == VPlexApiConstants.NOT_FOUND_STATUS) {
// return an empty list rather than an error
s_logger.info("VPLEX returned a 404 Not Found for this context, returning an empty list instead.");
return new ArrayList<VPlexStorageViewInfo>();
} else {
throw VPlexApiException.exceptions.failedGettingStorageViewInfo(String.valueOf(status));
}
}
/**
* Gets the fully-populated VPlexStorageViewInfo object for the given storage view name
* on the given VPLEX cluster (by cluster name, not cluster id). This method will
* return null if the storage view is not found on the VPLEX cluster (that is, if a 404 Not Found
* was returned by the VPLEX API).
*
* @param clusterName the cluster name for storage view scope
* @param storageViewName the name of the storage view to get
* @return a VPlexStorageViewInfo object for the storage view name and VPLEX cluster location
* or null if the storage view could not be found
* @throws VPlexApiException
*/
VPlexStorageViewInfo getStorageView(String clusterName, String storageViewName) throws VPlexApiException {
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_STORAGE_VIEWS.toString());
uriBuilder.append(storageViewName);
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Storage view request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI, VPlexApiConstants.ACCEPT_JSON_FORMAT_1);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status == VPlexApiConstants.SUCCESS_STATUS) {
try {
List<VPlexStorageViewInfo> storageViews = VPlexApiUtils
.getResourcesFromResponseContext(uriBuilder.toString(), responseStr,
VPlexStorageViewInfo.class);
if (storageViews != null && !storageViews.isEmpty()) {
if (storageViews.size() > 1) {
String message = "More than one VPLEX storage view was returned: " + storageViews;
s_logger.error(message);
throw new IllegalStateException(message);
}
VPlexStorageViewInfo storageView = storageViews.get(0);
if (storageView != null) {
storageView.refreshMaps();
storageView.setClusterId(clusterName);
updateStorageViewInitiatorPWWN(storageView);
s_logger.info("returning VPLEX storage view: " + storageView.toString());
return storageView;
}
}
} catch (Exception e) {
throw VPlexApiException.exceptions.getStorageViewsFailed(String.format(
"Error processing storage view: %s", e.getMessage()));
}
} else if (status == VPlexApiConstants.NOT_FOUND_STATUS) {
// fall through to return null rather than an error
s_logger.error("the VPLEX returned a 404 Not Found for storage view named {} on VPLEX cluster {}",
storageViewName, clusterName);
} else {
throw VPlexApiException.exceptions.getStorageViewsFailed(String.format(
"Failed getting storage view: %s", status));
}
s_logger.error("no storage view named {} found on VPLEX cluster {}", storageViewName, clusterName);
return null;
}
/**
* Finds the migrations with the passed names.
*
* @param migrationNames The names of the migrations to find.
*
* @return A list of references to the VPlex migration infos.
*
* @throws VPlexApiException If an error occurs trying to find a migration
* or a migration is simply not found.
*/
List<VPlexMigrationInfo> findMigrations(List<String> migrationNames)
throws VPlexApiException {
List<VPlexMigrationInfo> migrationInfoList = new ArrayList<VPlexMigrationInfo>();
for (String migrationName : migrationNames) {
try {
// First look in the device migrations and if not found, then
// look in the extent migrations.
VPlexMigrationInfo migrationInfo = findMigration(migrationName,
VPlexApiConstants.URI_DEVICE_MIGRATIONS);
migrationInfo.setIsDeviceMigration(true);
migrationInfoList.add(migrationInfo);
} catch (VPlexApiException vae) {
s_logger.info("Migration {} not found with device migrations");
// Try looking in the extent migrations.
// look in the extent migrations.
VPlexMigrationInfo migrationInfo = findMigration(migrationName,
VPlexApiConstants.URI_EXTENT_MIGRATIONS);
migrationInfo.setIsDeviceMigration(false);
migrationInfoList.add(migrationInfo);
}
}
return migrationInfoList;
}
/**
* Find the migration with the passed name.
*
* @param migrationName The name of the migration to find.
* @param baseMigrationPath The base path for this migration.
*
* @return A VPlex migration info.
*
* @throws VPlexApiException If an error occurs trying to find a migration
* or a migration is simply not found.
*/
VPlexMigrationInfo findMigration(String migrationName, URI baseMigrationPath)
throws VPlexApiException {
return findMigration(migrationName, baseMigrationPath, false);
}
/**
* Find the migration with the passed name.
*
* @param migrationName The name of the migration to find.
* @param baseMigrationPath The base path for this migration.
* @param retry Indicates retry should occur if the first attempt to find
* the migration fails.
*
* @return A VPlex migration info.
*
* @throws VPlexApiException If an error occurs trying to find a migration
* or a migration is simply not found.
*/
VPlexMigrationInfo findMigration(String migrationName, URI baseMigrationPath,
boolean retry) throws VPlexApiException {
int retryCount = 0;
VPlexMigrationInfo migrationInfo = null;
while (++retryCount <= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
try {
// Get the URI for the migration info request and make the request.
URI requestURI = _vplexApiClient.getBaseURI().resolve(baseMigrationPath);
s_logger.info("Find migration request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.getMigrationsFailureStatus(
String.valueOf(status), cause);
}
// Successful Response
List<VPlexMigrationInfo> allMigrationInfos = VPlexApiUtils
.getChildrenFromResponse(baseMigrationPath.toString(), responseStr,
VPlexMigrationInfo.class);
migrationInfo = null;
for (VPlexMigrationInfo mInfo : allMigrationInfos) {
s_logger.info("Migration Info: {}", mInfo.toString());
if (mInfo.getName().equals(migrationName)) {
migrationInfo = mInfo;
break;
}
}
if ((migrationInfo != null) || (!retry) || (retryCount >= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
// Throw an exception if not found.
if (migrationInfo == null) {
// Not found.
throw VPlexApiException.exceptions.cantFindMigrationWithName(migrationName);
} else {
// Now we update the migration info so it contains the status
// of the migration.
updateMigrationInfo(migrationInfo, baseMigrationPath);
break;
}
} else {
s_logger.warn("Migration not found on try {} of {}",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
}
} catch (VPlexApiException vae) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error(String.format("Exception finding migration on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), vae);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw vae;
}
} catch (Exception e) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error(String.format("Exception finding migration on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), e);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw VPlexApiException.exceptions.failureFindingMigrationWithName(migrationName, e);
}
}
}
return migrationInfo;
}
/**
* Updates the attribute info for the passed VPlex migration.
*
* @param portInfo The VPlex migration to update.
* @param baseMigrationPath The base path for these migrations.
*
* @throws VPlexApiException When an error occurs updating the migration
* attribute info.
*/
private void updateMigrationInfo(VPlexMigrationInfo migrationInfo,
URI baseMigrationPath) throws VPlexApiException {
// Get the URI for the migration info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(baseMigrationPath.toString());
uriBuilder.append(migrationInfo.getName());
URI requestURI = _vplexApiClient.getBaseURI().resolve(uriBuilder.toString());
s_logger.info("Migration Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(
String.format("Failed getting info for migration %s with status: %s",
migrationInfo.getName(), status));
}
// Now parse this response to populate the migration details in the passed
// migration info.
try {
VPlexApiUtils.setAttributeValues(responseStr, migrationInfo);
s_logger.info("Updated Migration Info {}", migrationInfo.toString());
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error updating migration information: %s", e.getMessage()), e);
}
}
/**
* Updates virtual volume details.
*
* @param clusterName the VPLEX cluster name
* @param virtualVolumeInfo the virtual volume to update
*
* @return boolean indicating whether or not the volume
* information was found on the device
*
* @throws VPlexApiException
*/
boolean updateVirtualVolumeInfo(String clusterName,
VPlexVirtualVolumeInfo virtualVolumeInfo) throws VPlexApiException {
// Get the URI for the virtual volume info request and make the request.
String virtualVolumeName = virtualVolumeInfo.getName();
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_VIRTUAL_VOLUMES.toString());
uriBuilder.append(virtualVolumeName);
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Virtual Volume Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status == VPlexApiConstants.NOT_FOUND_STATUS) {
s_logger.info("requested volume {} not found on vplex cluster {}",
virtualVolumeName, clusterName);
return false;
}
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw new VPlexApiException(String.format("Failed getting info for virtual "
+ "volume %s in cluster %s with status: %s", virtualVolumeName,
clusterName, status));
}
// Now parse this response to populate the virtual volume details in the
// passed system volume info.
try {
VPlexApiUtils.setAttributeValues(responseStr, virtualVolumeInfo);
s_logger
.info("Updated Virtual Volume Info {}", virtualVolumeInfo.toString());
} catch (Exception e) {
throw new VPlexApiException(String.format(
"Error processing system volume information: %s", e.getMessage()), e);
}
return true;
}
/**
* This method gets a virtual volume on the VPLEX directly by full context path.
*
* @param virtualVolumePath the virtual volume context path
* @return VPlexVirtualVolumeInfo object with full details
*
* @throws VPlexApiException
*/
VPlexVirtualVolumeInfo getVirtualVolumeByPath(String virtualVolumePath) throws VPlexApiException {
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(VPlexApiConstants.VPLEX_PATH + virtualVolumePath));
s_logger.info("Virtual Volume Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI, VPlexApiConstants.ACCEPT_JSON_FORMAT_1);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status == VPlexApiConstants.SUCCESS_STATUS) {
try {
List<VPlexVirtualVolumeInfo> vvols = VPlexApiUtils.getResourcesFromResponseContext(
virtualVolumePath, responseStr, VPlexVirtualVolumeInfo.class);
if (vvols != null && !vvols.isEmpty()) {
if (vvols.size() > 1) {
String message = "More than one VPLEX virtual volume was returned: " + vvols;
s_logger.error(message);
throw new IllegalStateException(message);
}
VPlexVirtualVolumeInfo virtualVolumeInfo = vvols.get(0);
if (virtualVolumeInfo != null) {
s_logger.info("returning VPLEX virtual volume: " + virtualVolumeInfo.toString());
return virtualVolumeInfo;
}
}
} catch (Exception e) {
s_logger.error("Exception encountered: ", e);
throw VPlexApiException.exceptions.errorProcessingVirtualVolumeInformation(e.getLocalizedMessage());
}
} else {
throw VPlexApiException.exceptions.failedGettingVirtualVolumeInfo(String.valueOf(status));
}
return null;
}
/**
* Gets all consistency groups on the VPLEX.
*
* NOTE: We want the list to be unique, but consistency group names
* must only be unique in the cluster. So two cluster could potentially
* have consistency groups with the same name. However, a consistency
* group created on one cluster can be given visibility to both clusters.
* In this case, we would find a group on the other cluster with the
* same name, but it would be the same actual consistency group. So,
* in cases were there is a name conflict across clusters we must check
* the visibility to determine if it is the same consistency group or
* a different one.
*
* @return A list of VPlexConsistencyGroupInfo instances corresponding to
* the consistency groups on the VPLEX.
*
* @throws VPlexApiException When an error occurs get the consistency
* groups.
*/
List<VPlexConsistencyGroupInfo> getConsistencyGroups()
throws VPlexApiException {
List<VPlexConsistencyGroupInfo> cgInfoList = new ArrayList<VPlexConsistencyGroupInfo>();
Map<String, VPlexConsistencyGroupInfo> cgMap = new HashMap<String, VPlexConsistencyGroupInfo>();
for (String clusterName : _vplexApiClient.getClusterIdToNameMap().values()) {
List<VPlexConsistencyGroupInfo> clusterCgs = getConsistencyGroupsOnCluster(clusterName);
for (VPlexConsistencyGroupInfo clusterCGInfo : clusterCgs) {
clusterCGInfo.setClusterName(clusterName);
String cgName = clusterCGInfo.getName();
if (!cgMap.containsKey(cgName)) {
cgInfoList.add(clusterCGInfo);
cgMap.put(clusterCGInfo.getName(), clusterCGInfo);
} else {
VPlexConsistencyGroupInfo cgInMap = cgMap.get(cgName);
if (!clusterCGInfo.hasClusterVisibility(cgInMap.getVisibility())) {
// The consistency group does not have the same visibility
// as the group with the same name already processed, so
// it is a different consistency group.
cgInfoList.add(clusterCGInfo);
}
}
}
}
return cgInfoList;
}
/**
* Gets consistency group info for the given VPLEX cluster.
*
* @param clusterName the name of the VPLEX cluster to look at
* @return a list of consistency group info objects for the given cluster
*
* @throws VPlexApiException
*/
List<VPlexConsistencyGroupInfo> getConsistencyGroupsOnCluster(String clusterName)
throws VPlexApiException {
s_logger.info("Getting all consistency groups from VPLEX at " + _vplexApiClient.getBaseURI().toString());
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_CGS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Get consistency groups request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI, VPlexApiConstants.ACCEPT_JSON_FORMAT_1);
String responseStr = response.getEntity(String.class);
int status = response.getStatus();
response.close();
if (status == VPlexApiConstants.SUCCESS_STATUS) {
try {
return VPlexApiUtils
.getResourcesFromResponseContext(uriBuilder.toString(), responseStr,
VPlexConsistencyGroupInfo.class);
} catch (Exception e) {
throw VPlexApiException.exceptions.errorProcessingConsistencyGroupInformation(e.getLocalizedMessage());
}
} else if (status == VPlexApiConstants.NOT_FOUND_STATUS) {
// return an empty list rather than an error
s_logger.info("VPLEX returned a 404 Not Found for this context, returning an empty list instead.");
return new ArrayList<VPlexConsistencyGroupInfo>();
} else {
throw VPlexApiException.exceptions.failedGettingConsistencyGroupInfo(String.valueOf(status));
}
}
/**
* Finds the consistency group with the passed name.
*
* @param cgName The name of the consistency group to find.
* @param clusterInfoList The cluster info.
* @param fetchAtts true to get the CG attributes.
*
* @return A reference to the VPlexConsistencyGroupInfo
*
* @throws VPlexApiException When an error occurs finding the consistency
* group or it is not found.
*/
VPlexConsistencyGroupInfo findConsistencyGroup(String cgName,
List<VPlexClusterInfo> clusterInfoList, boolean fetchAtts) throws VPlexApiException {
return findConsistencyGroup(cgName, clusterInfoList, fetchAtts, false);
}
/**
* Finds the consistency group with the passed name.
*
* @param cgName The name of the consistency group to find.
* @param clusterInfoList The cluster info.
* @param fetchAtts true to get the CG attributes.
* @param retry Indicates retry should occur if the first attempt to find
* the consistency group fails.
*
* @return A reference to the VPlexConsistencyGroupInfo
*
* @throws VPlexApiException When an error occurs finding the consistency
* group or it is not found.
*/
VPlexConsistencyGroupInfo findConsistencyGroup(String cgName,
List<VPlexClusterInfo> clusterInfoList, boolean fetchAtts, boolean retry)
throws VPlexApiException {
int retryCount = 0;
VPlexConsistencyGroupInfo cgInfo = null;
while (++retryCount <= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES) {
try {
cgInfo = null;
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
String clusterId = clusterInfo.getName();
List<VPlexConsistencyGroupInfo> allCGInfos = getConsistencyGroupsOnCluster(clusterId);
for (VPlexConsistencyGroupInfo info : allCGInfos) {
s_logger.info("Consistency Group Info: {}", info.toString());
if (info.getName().equals(cgName)) {
cgInfo = info;
cgInfo.setClusterName(clusterInfo.getName());
break;
}
}
// Found it.
if (cgInfo != null) {
break;
}
}
if ((cgInfo != null) || (!retry) || (retryCount >= VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
// Throw an exception if not found.
if (cgInfo == null) {
// Not found.
throw VPlexApiException.exceptions.didNotFindCGWithName(cgName);
} else if (fetchAtts) {
updateConsistencyGroupInfo(cgInfo);
}
break;
} else {
s_logger.warn("Consistency group not found on try {} of {}",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
}
} catch (VPlexApiException vae) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error(String.format("Exception finding consistency group on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), vae);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw vae;
}
} catch (Exception e) {
if ((retry) && (retryCount < VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES)) {
s_logger.error(String.format("Exception finding consistency group on try %d of %d",
retryCount, VPlexApiConstants.FIND_NEW_ARTIFACT_MAX_TRIES), e);
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_NEW_ARTIFACT_SLEEP_TIME_MS);
} else {
throw VPlexApiException.exceptions.failureFindingCGWithName(cgName, e);
}
}
}
return cgInfo;
}
/**
* Gets the attributes for the passed consistency group.
*
* @param cgInfo The consistency group info.
*
* @throws VPlexApiException When an error occurs getting the attributes for
* the consistency group.
*/
void updateConsistencyGroupInfo(VPlexConsistencyGroupInfo cgInfo)
throws VPlexApiException {
// Get the URI for the consistency group info request and make the
// request.
String cgName = cgInfo.getName();
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(cgInfo.getClusterName());
uriBuilder.append(VPlexApiConstants.URI_CGS.toString());
uriBuilder.append(cgName);
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Consistency group Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.failureUpdatingCGStatus(cgName,
cgInfo.getClusterName(), String.valueOf(status));
}
// Now parse this response to populate the consistency group details in
// the passed consistency group info.
try {
VPlexApiUtils.setAttributeValues(responseStr, cgInfo);
s_logger.info("Updated Consistency Group Info {}", cgInfo.toString());
} catch (Exception e) {
throw VPlexApiException.exceptions.failedUpdatingCG(cgName,
cgInfo.getClusterName(), e);
}
}
/**
* Causes the VPLEX to "forget" about the volumes identified by the
* passed native volume information. Typically called when the calling
* application has deleted backend volumes and wants the VPLEX to disregard
* these volumes.
*
* @param nativeVolumeInfoList The native volume information for the
* storage volumes to be forgotten.
*/
void forgetVolumes(List<VolumeInfo> nativeVolumeInfoList) throws Exception {
// For the volumes to be forgotten, map them by their
// storage system Guids.
Map<String, Set<String>> systemVolumesMap = new HashMap<String, Set<String>>();
for (VolumeInfo volumeInfo : nativeVolumeInfoList) {
String systemGuid = volumeInfo.getStorageSystemNativeGuid();
Set<String> systemVolumes = null;
if (systemVolumesMap.containsKey(systemGuid)) {
systemVolumes = systemVolumesMap.get(systemGuid);
} else {
systemVolumes = new HashSet<String>();
systemVolumesMap.put(systemGuid, systemVolumes);
}
systemVolumes.add(volumeInfo.getVolumeWWN());
}
// Rediscover the storage systems with volumes to be forgotten.
// When forgetting volumes, they have typically just been
// removed from the export group that exposes the volumes to
// the VPLEX. We need to rediscover the storage systems so the
// VPLEX sees that the storage volumes went away.
rediscoverStorageSystems(new ArrayList<String>(systemVolumesMap.keySet()));
// Sleep for a bit to be sure the VPlex completes the
// discovery prior to calling expand. Gets around
// an issue with the VPlex software not returning an
// Async code.
VPlexApiUtils.pauseThread(60000);
// Now find the context paths for the logical units that
// correspond to the volumes to be forgotten.
Map<String, Set<String>> notFoundSystemVolumesMap = new HashMap<String, Set<String>>();
Set<String> logUnitsPaths = findLogicalUnits(systemVolumesMap, notFoundSystemVolumesMap);
// If we don't find any of the logical units to forget throw
// an exception.
if (logUnitsPaths.isEmpty()) {
throw VPlexApiException.exceptions.logicalUnitsNotFoundForVolumes(systemVolumesMap.toString());
}
// Now tell the VPLEX to forget these logical units that were found.
try {
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_FORGET_LOG_UNIT);
s_logger.info("Forget logical units URI is {}", requestURI.toString());
StringBuilder argBuilder = new StringBuilder();
for (String logUnitPath : logUnitsPaths) {
if (argBuilder.length() != 0) {
argBuilder.append(",");
}
argBuilder.append(logUnitPath);
}
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_U, argBuilder.toString());
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false);
s_logger.info("Forget logical units POST data is {}",
postDataObject.toString());
ClientResponse response = _vplexApiClient.post(requestURI,
postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Forget logical units response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Forget volumes is completing asynchronously");
_vplexApiClient.waitForCompletion(response);
} else {
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
String errorMsg = String.format("Forget logical units failed with Status %s: %s",
response.getStatus(), cause);
throw new Exception(errorMsg);
}
}
// If we successfully forgot some volumes, but others were not found, then
// throw an exception identifying those that were not found.
if (!notFoundSystemVolumesMap.isEmpty()) {
throw VPlexApiException.exceptions.logicalUnitsNotFoundForVolumes(notFoundSystemVolumesMap.toString());
}
s_logger.info("Successfully forgot logical units");
} catch (Exception e) {
s_logger.error("Exception forgetting logical units: %s", e.getMessage(), e);
throw e;
}
}
/**
* Finds the context paths of the logical units corresponding to the passed
* volumes.
*
* @param systemVolumesMap A map of storage volume WWNs key'd by storage
* system.
* @param notFoundSystemVolumesMap OUT param holds info about logical units not found.
*
* @return The context paths of the passed volumes.
*/
private Set<String> findLogicalUnits(Map<String, Set<String>> systemVolumesMap,
Map<String, Set<String>> notFoundSystemVolumesMap) {
Set<String> logicalUnitPaths = new HashSet<String>();
Map<String, Set<String>> foundSystemVolumesMap = new HashMap<>();
// Get the cluster info.
List<VPlexClusterInfo> clusterInfoList = getClusterInfoLite();
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
// Get the storage systems for the cluster.
List<VPlexStorageSystemInfo> systemInfoList = getStorageSystemInfoForCluster(clusterInfo
.getName());
// Cycle over the storage systems and determine if it
// is one with storage volumes to be forgotten.
for (VPlexStorageSystemInfo systemInfo : systemInfoList) {
for (Entry<String, Set<String>> entry : systemVolumesMap.entrySet()) {
String systemGuid = entry.getKey();
if (systemInfo.matches(systemGuid)) {
// Get all logical units for this storage
// system.
Set<String> volumeWWNs = entry.getValue();
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterInfo.getName());
uriBuilder.append(VPlexApiConstants.URI_STORAGE_SYSTEMS.toString());
uriBuilder.append(systemInfo.getName());
uriBuilder.append(VPlexApiConstants.URI_LOGICAL_UNITS);
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Find logical units request URI is {}",
requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
s_logger.error("Failed getting logical units for context {}",
uriBuilder.toString());
}
// Successful Response
List<VPlexLogicalUnitInfo> logUnitInfoList = VPlexApiUtils
.getChildrenFromResponse(uriBuilder.toString(), responseStr,
VPlexLogicalUnitInfo.class);
// Cycle over the logical units and find the ones
// corresponding to the storage volumes to be
// forgotten for this storage system.
for (VPlexLogicalUnitInfo logUnitInfo : logUnitInfoList) {
String logUnitName = logUnitInfo.getName();
int indexWWNStart = logUnitName.indexOf(":") + 1;
String logUnitWWN = logUnitName.substring(indexWWNStart)
.toUpperCase();
if (volumeWWNs.contains(logUnitWWN)) {
// Add the logical unit context path
// to the list.
logicalUnitPaths.add(logUnitInfo.getPath());
// Add the volume to the found volumes map.
if (foundSystemVolumesMap.containsKey(systemGuid)) {
Set<String> foundVolumes = foundSystemVolumesMap.get(systemGuid);
foundVolumes.add(logUnitWWN);
} else {
Set<String> foundVolumes = new HashSet<>();
foundVolumes.add(logUnitWWN);
foundSystemVolumesMap.put(systemGuid, foundVolumes);
}
}
}
break;
}
}
}
}
// Use the found volumes map to determine those that where not found
// and populate the not found volumes map with the volumes for each
// system for which the corresponding logical unit was not found.
for (Entry<String, Set<String>> entry : systemVolumesMap.entrySet()) {
String systemGuid = entry.getKey();
Set<String> volumes = entry.getValue();
Set<String> notFoundVolumes = new HashSet<>(volumes);
if (foundSystemVolumesMap.containsKey(systemGuid)) {
Set<String> foundVolumes = foundSystemVolumesMap.get(systemGuid);
notFoundVolumes.removeAll(foundVolumes);
if (!notFoundVolumes.isEmpty()) {
notFoundSystemVolumesMap.put(systemGuid, notFoundVolumes);
}
} else {
// We did not find logical units for any of the volumes
// for this system.
notFoundSystemVolumesMap.put(systemGuid, notFoundVolumes);
}
}
return logicalUnitPaths;
}
/**
* Gets the virtual volume info for a given VPLEX cluster.
*
* @param clusterName the name of the VPLEX cluster to look at
*
* @return a list of VPlexVirtualVolumeInfo objects for the given VPLEX cluster
*
* @throws VPlexApiException
*/
List<VPlexVirtualVolumeInfo> getVirtualVolumesForCluster(String clusterName)
throws VPlexApiException {
s_logger.info("Getting all virtual volume information from VPLEX at "
+ _vplexApiClient.getBaseURI().toString());
// Get the URI for the virtual volume request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(clusterName);
uriBuilder.append(VPlexApiConstants.URI_VIRTUAL_VOLUMES.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Virtual volumes Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI, VPlexApiConstants.ACCEPT_JSON_FORMAT_1);
String responseStr = response.getEntity(String.class);
int status = response.getStatus();
response.close();
if (status == VPlexApiConstants.SUCCESS_STATUS) {
try {
return VPlexApiUtils.getResourcesFromResponseContext(
uriBuilder.toString(), responseStr, VPlexVirtualVolumeInfo.class);
} catch (Exception e) {
throw VPlexApiException.exceptions.errorProcessingVirtualVolumeInformation(e.getLocalizedMessage());
}
} else if (status == VPlexApiConstants.NOT_FOUND_STATUS) {
// return an empty list rather than an error
s_logger.info("VPLEX returned a 404 Not Found for this context, returning an empty list instead.");
return new ArrayList<VPlexVirtualVolumeInfo>();
} else {
throw VPlexApiException.exceptions.failedGettingVirtualVolumeInfo(String.valueOf(status));
}
}
/**
* Discovers and sets the component structure for the passed list
* of distributed virtual volumes.
*
* @param distVirtualVolumesMap A map of distributed virtual volumes
* keyed by the name of the distributed device that supports the
* virtual volume.
*/
void setSupportingComponentsForDistributedVirtualVolumes(
Map<String, VPlexVirtualVolumeInfo> distVirtualVolumesMap) {
// Discover and set the component structure for the distributed
// virtual volume. This is called when doing a deep discovery
// of the virtual volumes. If we get an error trying to get the
// component structure for the volume, we simply log an error.
// This allows the volume discovery to continue, and only the
// volumes for which the structure could be determined will
// be returned.
String virtualVolumeName = null;
try {
List<VPlexDistributedDeviceInfo> ddInfoList = getDistributedDeviceInfo();
for (VPlexDistributedDeviceInfo ddInfo : ddInfoList) {
String ddName = ddInfo.getName();
if (distVirtualVolumesMap.containsKey(ddName)) {
VPlexVirtualVolumeInfo virtualVolumeForDevice = distVirtualVolumesMap
.get(ddName);
virtualVolumeName = virtualVolumeForDevice.getName();
virtualVolumeForDevice.setSupportingDeviceInfo(ddInfo);
// use try/catch and move onto the next one on error
setSupportingComponentsForDistributedDevice(ddInfo);
}
}
} catch (Exception e) {
s_logger.error("An exception occured dicovering the component structure for " +
"distributed virtual volume {}", virtualVolumeName, e);
}
}
/**
* Discovers and sets the component structure for the passed list of local
* virtual volumes on the cluster with the passed id.
*
* @param localVirtualVolumesMap A map of local virtual volumes keyed by the
* name of the top level local device that supports the virtual
* volume.
*/
void setSupportingComponentsForLocalVirtualVolumes(String clusterId,
Map<String, VPlexVirtualVolumeInfo> localVirtualVolumesMap) {
// Discover and set the component structure for the local
// virtual volume. This is called when doing a deep discovery
// of the virtual volumes. If we get an error trying to get the
// component structure for the volume, we simply log an error.
// This allows the volume discovery to continue, and only the
// volumes for which the structure could be determined will
// be returned.
String virtualVolumeName = null;
try {
List<VPlexDeviceInfo> deviceInfoList = getLocalDeviceInfoOnCluster(clusterId);
for (VPlexDeviceInfo deviceInfo : deviceInfoList) {
String deviceName = deviceInfo.getName();
if (localVirtualVolumesMap.containsKey(deviceName)) {
deviceInfo.setCluster(clusterId);
VPlexVirtualVolumeInfo virtualVolumeForDevice = localVirtualVolumesMap.get(deviceName);
virtualVolumeName = virtualVolumeForDevice.getName();
virtualVolumeForDevice.setSupportingDeviceInfo(deviceInfo);
// use try/catch and move onto the next one on error
setSupportingComponentsForLocalDevice(deviceInfo);
}
}
} catch (Exception e) {
s_logger.error("An exception occured dicovering the component structure for " +
"local virtual volume {}", virtualVolumeName, e);
}
}
/**
* Gets the distributed devices for the VPLEX.
*
* @return A list of VPlexDistributedDeviceInfo representing the distributed
* devices.
*
* @throws VPlexApiException When an error occurs getting the distributed
* device information.
*/
List<VPlexDistributedDeviceInfo> getDistributedDeviceInfo()
throws VPlexApiException {
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_DISTRIBUTED_DEVICES);
s_logger.info("Distributed devices Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.debug("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions
.failureGettingDistributedDevicesStatus(String.valueOf(status));
}
try {
List<VPlexDistributedDeviceInfo> ddInfoList = VPlexApiUtils
.getChildrenFromResponse(
VPlexApiConstants.URI_DISTRIBUTED_DEVICES.toString(), responseStr,
VPlexDistributedDeviceInfo.class);
return ddInfoList;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedGettingDistributedDevices(e);
}
}
/**
* Discovers and sets the supporting components (i.e., top level local
* devices) for the passed distributed device.
*
* @param ddInfo The distributed device information.
*
* @throws VPlexApiException When an error occurs discovering the supporting
* components for the distributed device.
*/
void setSupportingComponentsForDistributedDevice(VPlexDistributedDeviceInfo ddInfo)
throws VPlexApiException {
List<VPlexDeviceInfo> childDeviceInfoList = new ArrayList<VPlexDeviceInfo>();
List<VPlexDistributedDeviceComponentInfo> componentsInfoList = getDistributedDeviceComponents(ddInfo);
for (VPlexDistributedDeviceComponentInfo componentInfo : componentsInfoList) {
updateDistributedDeviceComponent(componentInfo);
VPlexDeviceInfo childDeviceInfo = new VPlexDeviceInfo();
childDeviceInfo.setName(componentInfo.getName());
childDeviceInfo.setPath(componentInfo.getPath());
childDeviceInfo.setType(VPlexResourceInfo.ResourceType.LOCAL_DEVICE.getResourceType());
childDeviceInfo.setCluster(componentInfo.getCluster());
childDeviceInfoList.add(childDeviceInfo);
setSupportingComponentsForLocalDevice(childDeviceInfo);
}
ddInfo.setLocalDeviceInfo(childDeviceInfoList);
}
/**
* Gets the supporting components for the passed distributed device.
*
* @param ddInfo The distributed device information.
*
* @return A list of VPlexDistributedDeviceComponentInfo representing the
* supporting components of the distributed device.
*
* @throws VPlexApiException When an error occurs getting the distributed
* device components.
*/
List<VPlexDistributedDeviceComponentInfo> getDistributedDeviceComponents(
VPlexDistributedDeviceInfo ddInfo) throws VPlexApiException {
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.VPLEX_PATH);
uriBuilder.append(ddInfo.getPath());
uriBuilder.append(VPlexApiConstants.URI_DISTRIBUTED_DEVICE_COMP);
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Get distributed device components request URI is {}",
requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Get distributed device components response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions
.failureGettingDistDeviceComponentsStatus(ddInfo.getPath(),
String.valueOf(status));
}
try {
List<VPlexDistributedDeviceComponentInfo> componentInfoList = VPlexApiUtils
.getChildrenFromResponse(uriBuilder.toString(), responseStr,
VPlexDistributedDeviceComponentInfo.class);
return componentInfoList;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedGettingDistDeviceComponents(
ddInfo.getPath(), e);
}
}
/**
* Gets the attributes for the passed distributed device component.
*
* @param componentInfo The distributed device component info
*
* @throws VPlexApiException When an error occurs getting the attributes for
* the distributed device component.
*/
void updateDistributedDeviceComponent(
VPlexDistributedDeviceComponentInfo componentInfo) throws VPlexApiException {
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.VPLEX_PATH);
uriBuilder.append(componentInfo.getPath());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Distributed device component request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.updateDistDeviceComponentFailureStatus(
componentInfo.getPath(), String.valueOf(status));
}
try {
VPlexApiUtils.setAttributeValues(responseStr, componentInfo);
s_logger.info("Updated Distributed Device Component Info {}", componentInfo.toString());
} catch (Exception e) {
throw VPlexApiException.exceptions.failedUpdateDistDeviceComponentInfo(
componentInfo.getPath(), e);
}
}
/**
* Discovers and sets the supporting components (i.e., other local
* devices and extents) for the passed local device.
*
* @param deviceInfo The local device information.
*
* @throws VPlexApiException When an error occurs discovering the supporting
* components for the local device.
*/
void setSupportingComponentsForLocalDevice(VPlexDeviceInfo deviceInfo)
throws VPlexApiException {
List<VPlexExtentInfo> extentInfoList = new ArrayList<VPlexExtentInfo>();
List<VPlexDeviceInfo> childDeviceInfoList = new ArrayList<VPlexDeviceInfo>();
List<VPlexLocalDeviceComponentInfo> componentInfoList = getLocalDeviceComponents(deviceInfo);
for (VPlexLocalDeviceComponentInfo componentInfo : componentInfoList) {
updateLocalDeviceComponent(componentInfo);
String componentType = componentInfo.getComponentType();
if (VPlexResourceInfo.ResourceType.EXTENT.getResourceType().equals(
componentType)) {
VPlexExtentInfo extentInfo = new VPlexExtentInfo();
extentInfo.setName(componentInfo.getName());
extentInfo.setPath(componentInfo.getPath());
extentInfo.setType(componentType);
extentInfo.setClusterId(deviceInfo.getCluster());
extentInfoList.add(extentInfo);
setSupportingComponentsForExtent(extentInfo);
} else {
VPlexDeviceInfo childDeviceInfo = new VPlexDeviceInfo();
childDeviceInfo.setName(componentInfo.getName());
childDeviceInfo.setPath(componentInfo.getPath());
childDeviceInfo.setType(componentType);
childDeviceInfo.setCluster(deviceInfo.getCluster());
childDeviceInfoList.add(childDeviceInfo);
setSupportingComponentsForLocalDevice(childDeviceInfo);
}
}
deviceInfo.setExtentInfo(extentInfoList);
deviceInfo.setChildDeviceInfo(childDeviceInfoList);
}
/**
* Gets the supporting components for the passed local device.
*
* @param localDeviceInfo The local device information.
*
* @return A list of VPlexLocalDeviceComponentInfo representing the
* supporting components of the local device.
*
* @throws VPlexApiException When an error occurs getting the local device
* components.
*/
List<VPlexLocalDeviceComponentInfo> getLocalDeviceComponents(
VPlexResourceInfo localDeviceInfo) throws VPlexApiException {
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.VPLEX_PATH);
uriBuilder.append(localDeviceInfo.getPath());
uriBuilder.append(VPlexApiConstants.URI_COMPONENTS);
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Get components for local device URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Get components for local device response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions
.failureGettingComponentsForLocalDeviceStatus(localDeviceInfo.getPath(),
String.valueOf(status));
}
try {
List<VPlexLocalDeviceComponentInfo> componentInfoList = VPlexApiUtils
.getChildrenFromResponse(uriBuilder.toString(), responseStr,
VPlexLocalDeviceComponentInfo.class);
return componentInfoList;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedGettingComponentsForLocalDevice(
localDeviceInfo.getPath(), e);
}
}
/**
* Gets the attributes for the passed local device component.
*
* @param componentInfo The local device component info
*
* @throws VPlexApiException When an error occurs getting the attributes for
* the local device component.
*/
void updateLocalDeviceComponent(VPlexLocalDeviceComponentInfo componentInfo)
throws VPlexApiException {
// Get the URI for the cluster info request and make the request.
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.VPLEX_PATH);
uriBuilder.append(componentInfo.getPath());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Local device component request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.updatelLocalDeviceComponentFailureStatus(
componentInfo.getPath(), String.valueOf(status));
}
try {
VPlexApiUtils.setAttributeValues(responseStr, componentInfo);
s_logger.info("Updated Local Device Component Info {}", componentInfo.toString());
} catch (Exception e) {
throw VPlexApiException.exceptions.failedUpdateLocalDeviceComponentInfo(
componentInfo.getPath(), e);
}
}
/**
* Discovers and sets the supporting components (i.e., storage volume) for
* the passed local device.
*
* @param extentInfo The extent information.
*
* @throws VPlexApiException When an error occurs discovering the supporting
* components for the extent.
*/
void setSupportingComponentsForExtent(VPlexExtentInfo extentInfo)
throws VPlexApiException {
// An extent should have a single component which is the storage volume
// on which it was built.
List<VPlexResourceInfo> componentInfoList = getExtentComponents(extentInfo);
if (componentInfoList.size() == 1) {
VPlexResourceInfo componentInfo = componentInfoList.get(0);
VPlexStorageVolumeInfo storageVolumeInfo = new VPlexStorageVolumeInfo();
storageVolumeInfo.setName(componentInfo.getName());
storageVolumeInfo.setPath(componentInfo.getPath());
storageVolumeInfo.setType(VPlexResourceInfo.ResourceType.STORAGE_VOLUME
.getResourceType());
storageVolumeInfo.setClusterId(extentInfo.getClusterId());
extentInfo.setStorageVolumeInfo(storageVolumeInfo);
} else {
throw VPlexApiException.exceptions.moreThanOneComponentForExtent(extentInfo
.getPath());
}
}
/**
* Gets the supporting components for the passed extent.
*
* @param extentInfo The extent information.
*
* @return A list of VPlexResourceInfo representing the supporting
* components of the extent.
*
* @throws VPlexApiException When an error occurs getting the extent
* components.
*/
List<VPlexResourceInfo> getExtentComponents(VPlexExtentInfo extentInfo)
throws VPlexApiException {
StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append(VPlexApiConstants.VPLEX_PATH);
uriBuilder.append(extentInfo.getPath());
uriBuilder.append(VPlexApiConstants.URI_COMPONENTS);
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(uriBuilder.toString()));
s_logger.info("Get components for extent URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Get components for extent response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.failureGettingExtentComponentsStatus(
extentInfo.getName(), String.valueOf(status));
}
try {
List<VPlexResourceInfo> componentInfoList = VPlexApiUtils
.getChildrenFromResponse(uriBuilder.toString(), responseStr,
VPlexResourceInfo.class);
return componentInfoList;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedGettingExtentComponents(
extentInfo.getName(), e);
}
}
/**
* Returns a map of Initiator WWN to Initiator Name
*
* @param clusterName VPlex cluster ID
* @param fetchFromArray {@link Boolean} if true passed, Fetch the initiator information from array and updates the local cache
* @return map of Initiator WWN to Initiator Name
*/
Map<String, String> getInitiatorWwnToNameMap(String clusterName, boolean fetchFromArray) {
Map<String, String> result = new HashMap<String, String>();
List<VPlexInitiatorInfo> initiatorInfoList = getInitiatorInfoForCluster(clusterName, fetchFromArray);
for (VPlexInitiatorInfo initiatorInfo : initiatorInfoList) {
if (initiatorInfo.getName() != null
&& initiatorInfo.getPortWwn() != null) {
result.put(initiatorInfo.getPortWwn(), initiatorInfo.getName());
}
}
return result;
}
/**
* Returns a map of Initiator Name to Initiator WWN
*
* @param clusterName VPlex cluster ID
* @param fetchFromArray {@link Boolean} if true passed, Fetch the initiator information from array and updates the local cache
* @return map of Initiator Name to Initiator WWN
*/
Map<String, String> getInitiatorNameToWwnMap(String clusterName, boolean fetchFromArray) {
Map<String, String> result = new HashMap<String, String>();
List<VPlexInitiatorInfo> initiatorInfoList;
initiatorInfoList = getInitiatorInfoForCluster(clusterName, fetchFromArray);
for (VPlexInitiatorInfo initiatorInfo : initiatorInfoList) {
if (initiatorInfo.getName() != null
&& initiatorInfo.getPortWwn() != null) {
result.put(initiatorInfo.getName(), initiatorInfo.getPortWwn());
}
}
return result;
}
/**
* Attempts to refresh the given VPLEX contexts.
*
* @param contexts The list of context paths to be refreshed.
*/
void refreshContexts(List<String> contexts) {
// Create the request.
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_REFRESH_CONTEXT);
s_logger.info("Refresh contexts URI is {}", requestURI.toString());
// Build the post data specifying the contexts to be refreshed.
Map<String, String> argsMap = new HashMap<String, String>();
StringBuilder argsBuilder = new StringBuilder();
for (String context : contexts) {
if (argsBuilder.length() != 0) {
argsBuilder.append(",");
}
argsBuilder.append(context);
}
argsMap.put(VPlexApiConstants.ARG_DASH_C, argsBuilder.toString());
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false);
s_logger.info("Refresh contexts POST data is {}", postDataObject.toString());
// Execute the request to refresh the passed contexts.
ClientResponse response = null;
try {
response = _vplexApiClient.post(requestURI, postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Refresh contexts response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Refresh contexts is completing asynchrounously");
_vplexApiClient.waitForCompletion(response);
} else {
s_logger.warn("Refresh of contexts {} failed", contexts);
}
}
s_logger.info("Refresh contexts was successful");
} catch (Exception e) {
s_logger.warn("Exception during context refresh", e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Returns a List of VPlexStorageVolumeInfo storage volumes for the given
* device name, locality (virtual volume type), and cluster name. If it's
* determined the top-level device is mirrored (i.e., has two child devices
* in a RAID-1 configuration), then the VPLEX API request URI will go one
* level deeper.
*
* @param deviceName the top-level device name to query on
* @param virtualVolumeType the virtual volume type (local or distributed)
* @param clusterName the cluster name
* @param hasMirror if the top level device is mirrored
*
* @return a list of VPlexStorageVolumeInfo storage volumes comprising the device
* @throws VPlexApiException
*/
public List<VPlexStorageVolumeInfo> getStorageVolumesForDevice(
String deviceName, String virtualVolumeType, String clusterName, boolean hasMirror) throws VPlexApiException {
long start = System.currentTimeMillis();
s_logger.info("Getting backend storage volume wwn info for {} volume {} from VPLEX at "
+ _vplexApiClient.getBaseURI().toString(), virtualVolumeType, deviceName);
StringBuilder uriBuilder = new StringBuilder();
if (VPlexApiConstants.LOCAL_VIRTUAL_VOLUME.equals(virtualVolumeType)) {
// format /vplex/clusters/*/devices
// /DEVICE_NAME/components/*/components/*
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
if (null != clusterName && !clusterName.isEmpty()) {
uriBuilder.append(clusterName);
} else {
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
}
uriBuilder.append(VPlexApiConstants.URI_DEVICES.toString());
uriBuilder.append(deviceName);
uriBuilder.append(VPlexApiConstants.URI_COMPONENTS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
uriBuilder.append(VPlexApiConstants.URI_COMPONENTS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
if (hasMirror) {
uriBuilder.append(VPlexApiConstants.URI_COMPONENTS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
}
} else if (VPlexApiConstants.DISTRIBUTED_VIRTUAL_VOLUME.equals(virtualVolumeType)) {
// format /vplex/distributed-storage/distributed-devices
// /DEVICE_NAME/distributed-device-components/*/components/*/components/*
uriBuilder.append(VPlexApiConstants.URI_DISTRIBUTED_DEVICES.toString());
uriBuilder.append(deviceName);
uriBuilder.append(VPlexApiConstants.URI_DISTRIBUTED_DEVICE_COMP.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
uriBuilder.append(VPlexApiConstants.URI_COMPONENTS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
uriBuilder.append(VPlexApiConstants.URI_COMPONENTS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
if (hasMirror) {
uriBuilder.append(VPlexApiConstants.URI_COMPONENTS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
}
} else {
String reason = "invalid VPLEX locality for device " + deviceName + ": " + virtualVolumeType;
s_logger.error(reason);
throw VPlexApiException.exceptions.failedGettingStorageVolumeInfoForIngestion(reason);
}
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Storage Volume Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI,
VPlexApiConstants.ACCEPT_JSON_FORMAT_1,
VPlexApiConstants.CACHE_CONTROL_MAXAGE_DEFAULT_VALUE);
String responseStr = response.getEntity(String.class);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
s_logger.error(responseStr);
throw VPlexApiException.exceptions.failedGettingStorageVolumeInfoForIngestion(String.valueOf(status));
}
// Successful Response
try {
List<VPlexStorageVolumeInfo> storageVolumeInfoList = VPlexApiUtils.getResourcesFromResponseContext(uriBuilder.toString(),
responseStr, VPlexStorageVolumeInfo.class);
s_logger.info("TIMER: getStorageVolumesForDevice took {}ms",
System.currentTimeMillis() - start);
return storageVolumeInfoList;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedGettingStorageVolumeInfoForIngestion(e.getLocalizedMessage());
}
}
/**
* Returns the top-level supporting device name for a given storage with the passed
* native storage volume information. Uses the storage-volume used-by VPLEX CLI
* command to do a reverse look up of the parent device for a storage volume.
*
* @param vInfo The native storage volume information.
*
* @return the name of the top level device for the given storage volume
* @throws VPlexApiException
*/
@Deprecated
public String getDeviceForStorageVolume(VolumeInfo vInfo) throws VPlexApiException {
long start = System.currentTimeMillis();
s_logger.info("Getting device name for array {} volume {} (wwn: {}) from VPLEX at "
+ vInfo.getStorageSystemNativeGuid(), vInfo.getVolumeNativeId(), vInfo.getVolumeWWN());
StringBuilder contextArgBuilder = new StringBuilder();
// format /storage-volume+used-by
// payload {"args":"-d \/clusters\/*\/storage-elements\/storage-volumes\/*APM00140844981*01735"}
contextArgBuilder.append(VPlexApiConstants.URI_CLUSTERS_RELATIVE.toString());
contextArgBuilder.append(VPlexApiConstants.WILDCARD.toString());
contextArgBuilder.append(VPlexApiConstants.URI_STORAGE_VOLUMES.toString());
String contextArg = contextArgBuilder.toString();
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(VPlexApiConstants.URI_STORAGE_VOLUME_USED_BY.toString()));
s_logger.info("Find device for storage volume request URI is {}", requestURI.toString());
// the following will try to find a storage-volume used-by structure
// based on several possible storage-volume element name patterns as defined
// in the getVolumeNamePattern method below. this is a best effort.
// this uses the regex/wildcard name matching feature for context names
// to try to find a storage volume based on the native id, wwn, and array serial number
// the max number of patterns possibly returned by getVolumeNamePattern
int numPatterns = 3;
boolean success = false;
String responseStr = "";
for (int i = 0; i < numPatterns; i++) {
String pattern = getVolumeNamePattern(i, vInfo);
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_D, contextArg + pattern);
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false);
s_logger.info("Find device for storage volume POST data is {}",
postDataObject.toString());
ClientResponse response = _vplexApiClient.post(requestURI,
postDataObject.toString());
responseStr = response.getEntity(String.class);
s_logger.info("Find device for storage volume response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
// try another pattern
continue;
} else {
success = true;
break;
}
}
if (!success) {
throw VPlexApiException.exceptions
.failedGettingDeviceNameForStorageVolume("no volume name patterns worked");
}
// Successful Response
String customData = VPlexApiUtils.getCustomDataFromResponse(responseStr);
// this custom data parsing hackage is very uncomfortable...
// the response payload comes back in a really grungy format like this:
// /clusters/cluster-1/devices/device_VAPM00140844981-01735:\n
// extent_VAPM00140844981-01735_1\n
// VAPM00140844981-01735\n\n
String deviceName = null;
try {
s_logger.info("custom data is " + customData);
String[] lines = customData.split(":\n");
s_logger.info("device context is: " + lines[0]);
String[] subLines = lines[0].split("/");
deviceName = subLines[subLines.length - 1];
s_logger.info("returning device name: " + deviceName);
} catch (Exception ex) {
String reason = "could not parse custom data string to find device name: " + customData;
s_logger.error(reason);
throw VPlexApiException.exceptions
.failedGettingDeviceNameForStorageVolume(reason);
}
s_logger.info("TIMER: getDeviceForStorageVolume took {}ms",
System.currentTimeMillis() - start);
return deviceName;
}
/**
* A storage-volume name pattern generator.
*
* @param i the pattern number to get
* @param vInfo The native storage volume information.
* @return
*/
@Deprecated
private String getVolumeNamePattern(int i, VolumeInfo vInfo) {
String pattern = "";
switch (i) {
case 0:
pattern = VPlexApiConstants.WILDCARD
+ vInfo.getVolumeName();
break;
case 1:
// *[wwn] (seems by default vols get VPD83T3:wwn for a name)
pattern = VPlexApiConstants.WILDCARD
+ vInfo.getVolumeWWN()
+ VPlexApiConstants.WILDCARD;
break;
case 2:
// *[wwn].toLowerCase (the used-by command seems to be case-sensitive,
// and many vol names are lower case)
pattern = VPlexApiConstants.WILDCARD
+ vInfo.getVolumeWWN().toLowerCase()
+ VPlexApiConstants.WILDCARD;
break;
}
return pattern;
}
/**
* Returns a VPlexDistributedDeviceInfo object for the given device. For each leg,
* the device geometry (RAID configuration) is analyzed to ensure an acceptable component
* type for ingestion is present. RAID-0 is acceptable as is. If RAID-1 is found,
* then the children will need to be analyzed to make sure they are composed
* of only RAID-0 devices (by calling getDeviceComponentInfoForIngestion).
* RAID-C volumes at this level will be rejected.
*
* @param deviceName the name of the device
*
* @return a VPlexResourceInfo object for the device name
* @throws VPlexApiException
*/
public VPlexDistributedDeviceInfo getDeviceStructureForDistributedIngestion(
String deviceName) throws VPlexApiException {
long start = System.currentTimeMillis();
s_logger.info("Getting device structure info for device {} from VPLEX at "
+ _vplexApiClient.getBaseURI().toString(), deviceName);
StringBuilder uriBuilder = new StringBuilder();
// format /vplex/distributed-storage/distributed-devices
// /DEVICE_NAME/distributed-device-components/*
uriBuilder.append(VPlexApiConstants.URI_DISTRIBUTED_DEVICES.toString());
uriBuilder.append(deviceName);
uriBuilder.append(VPlexApiConstants.URI_DISTRIBUTED_DEVICE_COMP.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Distributed Device Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI,
VPlexApiConstants.ACCEPT_JSON_FORMAT_1,
VPlexApiConstants.CACHE_CONTROL_MAXAGE_DEFAULT_VALUE);
String responseStr = response.getEntity(String.class);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.failedGettingDeviceStructure(String.valueOf(status));
}
VPlexDistributedDeviceInfo parentDevice = new VPlexDistributedDeviceInfo();
parentDevice.setName(deviceName);
// Successful Response
List<VPlexDeviceInfo> deviceInfoList = VPlexApiUtils.getResourcesFromResponseContext(uriBuilder.toString(),
responseStr, VPlexDeviceInfo.class);
for (VPlexDeviceInfo componentDevice : deviceInfoList) {
switch (componentDevice.getGeometry().toLowerCase()) {
case VPlexApiConstants.ARG_GEOMETRY_RAID0:
s_logger.info("top-level device geometry is raid-0 for component {}, no further info needed",
componentDevice.getName());
break;
case VPlexApiConstants.ARG_GEOMETRY_RAID1:
s_logger.info("top-level device geometry is raid-1 for component {}, need to find mirror info",
componentDevice.getName());
List<VPlexDeviceInfo> childDeviceInfos = getDeviceComponentInfoForIngestion(componentDevice);
componentDevice.setChildDeviceInfo(childDeviceInfos);
break;
case VPlexApiConstants.ARG_GEOMETRY_RAIDC:
default:
String reason = "invalid component device geometry "
+ componentDevice.getGeometry() + " for component " + componentDevice.getName();
s_logger.error(reason);
throw VPlexApiException.exceptions.deviceStructureIsIncompatibleForIngestion(reason);
}
}
parentDevice.setGeometry(VPlexApiConstants.ARG_GEOMETRY_RAID1);
parentDevice.setLocalDeviceInfo(deviceInfoList);
if (!deviceInfoList.isEmpty()) {
s_logger.info("found these distributed component devices for VPLEX device {}:", parentDevice.getName());
for (VPlexDeviceInfo info : deviceInfoList) {
s_logger.info(info.toString());
}
}
s_logger.info("TIMER: getDeviceStructureForDistributedIngestion took {}ms",
System.currentTimeMillis() - start);
return parentDevice;
}
/**
* Returns a VPlexDeviceInfo object for the given device. The device geometry
* (RAID configuration) is analyzed to ensure an acceptable component type
* for ingestion is present. RAID-0 is acceptable as is. If RAID-1 is found,
* then the children will need to be analyzed to make sure they are composed
* of only RAID-0 devices (by calling getDeviceComponentInfoForIngestion).
* RAID-C volumes at this level will be rejected.
*
* @param deviceName the name of the device
*
* @return a VPlexResourceInfo object for the device name
* @throws VPlexApiException
*/
public VPlexDeviceInfo getDeviceStructureForLocalIngestion(
String deviceName) throws VPlexApiException {
long start = System.currentTimeMillis();
s_logger.info("Getting device structure info for device {} from VPLEX at "
+ _vplexApiClient.getBaseURI().toString(), deviceName);
StringBuilder uriBuilder = new StringBuilder();
// format /vplex/clusters/*/devices/DEVICE_NAME
uriBuilder.append(VPlexApiConstants.URI_CLUSTERS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
uriBuilder.append(VPlexApiConstants.URI_DEVICES.toString());
uriBuilder.append(deviceName);
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Local Device Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI,
VPlexApiConstants.ACCEPT_JSON_FORMAT_1,
VPlexApiConstants.CACHE_CONTROL_MAXAGE_DEFAULT_VALUE);
String responseStr = response.getEntity(String.class);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.failedGettingDeviceStructure(String.valueOf(status));
}
VPlexDeviceInfo device = null;
// Successful Response
List<VPlexDeviceInfo> deviceInfoList = VPlexApiUtils.getResourcesFromResponseContext(uriBuilder.toString(),
responseStr, VPlexDeviceInfo.class);
if (deviceInfoList.size() == 1) {
device = deviceInfoList.get(0);
switch (device.getGeometry().toLowerCase()) {
case VPlexApiConstants.ARG_GEOMETRY_RAID0:
s_logger.info("top-level device geometry is raid-0 for device {}, no further info needed",
device.getName());
break;
case VPlexApiConstants.ARG_GEOMETRY_RAID1:
s_logger.info("top-level device geometry is raid-1 for device {}, finding children",
device.getName());
List<VPlexDeviceInfo> componentDeviceInfoList = getDeviceComponentInfoForIngestion(device);
device.setChildDeviceInfo(componentDeviceInfoList);
break;
case VPlexApiConstants.ARG_GEOMETRY_RAIDC:
default:
String reason = "invalid component device geometry "
+ device.getGeometry() + " for component " + device.getName();
s_logger.error(reason);
throw VPlexApiException.exceptions.deviceStructureIsIncompatibleForIngestion(reason);
}
} else {
String reason = "invalid top level device configuration";
s_logger.error(reason);
throw VPlexApiException.exceptions.deviceStructureIsIncompatibleForIngestion(reason);
}
if (!deviceInfoList.isEmpty()) {
s_logger.info("found this local device component info for VPLEX device {}:", deviceName);
for (VPlexDeviceInfo info : deviceInfoList) {
s_logger.info(info.toString());
}
}
s_logger.info("TIMER: getDeviceStructureForLocalIngestion took {}ms",
System.currentTimeMillis() - start);
return device;
}
/**
* Returns a List of child VPlexDeviceInfo components for a given
* VPlexDeviceInfo parent device. The device geometry (RAID configuration)
* is analyzed to ensure an acceptable component type for ingestion
* is present. Only RAID-0 is acceptable for a child device. RAID-1 and
* RAID-C at this level will be rejected for ingestion purposes.
*
* @param parentDevice the parent VPlexDeviceInfo
*
* @return a List of child VPlexDeviceInfo objects for the parent
* @throws VPlexApiException
*/
public List<VPlexDeviceInfo> getDeviceComponentInfoForIngestion(
VPlexDeviceInfo parentDevice) throws VPlexApiException {
long start = System.currentTimeMillis();
s_logger.info("Getting device component info for {} from VPLEX at "
+ _vplexApiClient.getBaseURI().toString(), parentDevice.getName());
StringBuilder uriBuilder = new StringBuilder();
// /vplex/clusters/cluster-1/devices/device_VAPM00140844981-01736/components/*
uriBuilder.append(VPlexApiConstants.VPLEX_PATH);
uriBuilder.append(parentDevice.getPath());
uriBuilder.append(VPlexApiConstants.URI_COMPONENTS.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Child Device Component Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI,
VPlexApiConstants.ACCEPT_JSON_FORMAT_1,
VPlexApiConstants.CACHE_CONTROL_MAXAGE_DEFAULT_VALUE);
String responseStr = response.getEntity(String.class);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
throw VPlexApiException.exceptions.failedGettingDeviceStructure(String.valueOf(status));
}
// Successful Response
List<VPlexDeviceInfo> deviceInfoList = VPlexApiUtils.getResourcesFromResponseContext(uriBuilder.toString(),
responseStr, VPlexDeviceInfo.class);
for (VPlexDeviceInfo device : deviceInfoList) {
switch (device.getGeometry().toLowerCase()) {
case VPlexApiConstants.ARG_GEOMETRY_RAID0:
s_logger.info("component device geometry is raid-0 for device {}, no further info needed",
device.getName());
break;
case VPlexApiConstants.ARG_GEOMETRY_RAID1:
case VPlexApiConstants.ARG_GEOMETRY_RAIDC:
default:
String reason = "invalid component device geometry "
+ device.getGeometry() + " for component " + device.getName();
s_logger.error(reason);
throw VPlexApiException.exceptions.deviceStructureIsIncompatibleForIngestion(reason);
}
}
if (!deviceInfoList.isEmpty()) {
s_logger.info("found these child devices for VPLEX device {}:", parentDevice.getName());
for (VPlexDeviceInfo info : deviceInfoList) {
s_logger.info(info.toString());
}
}
s_logger.info("TIMER: getDeviceComponentInfoForIngestion took {}ms",
System.currentTimeMillis() - start);
return deviceInfoList;
}
/**
* Returns a Map of distributed device component context
* paths from the VPLEX API to VPLEX cluster names.
*
* @return a Map of distributed device component context
* paths from the VPLEX API to VPLEX cluster names
*
* @throws VPlexApiException
*/
public Map<String, String> getDistributedDevicePathToClusterMap()
throws VPlexApiException {
long start = System.currentTimeMillis();
s_logger.info("Getting distributed device path to cluster id map from VPLEX at "
+ _vplexApiClient.getBaseURI().toString());
StringBuilder uriBuilder = new StringBuilder();
// format /vplex/distributed-storage/distributed-devices/*/distributed-device-components/*
uriBuilder.append(VPlexApiConstants.URI_DISTRIBUTED_DEVICES.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
uriBuilder.append(VPlexApiConstants.URI_DISTRIBUTED_DEVICE_COMP.toString());
uriBuilder.append(VPlexApiConstants.WILDCARD.toString());
URI requestURI = _vplexApiClient.getBaseURI().resolve(URI.create(uriBuilder.toString()));
s_logger.info("Distributed Device Component Info Request URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.get(requestURI,
VPlexApiConstants.ACCEPT_JSON_FORMAT_1,
VPlexApiConstants.CACHE_CONTROL_MAXAGE_DEFAULT_VALUE);
String responseStr = response.getEntity(String.class);
int status = response.getStatus();
response.close();
Map<String, String> distributedDevicePathToClusterMap = new HashMap<String, String>();
if (status == VPlexApiConstants.SUCCESS_STATUS) {
// Successful Response
List<VPlexDeviceInfo> deviceInfoList = VPlexApiUtils.getResourcesFromResponseContext(uriBuilder.toString(),
responseStr, VPlexDeviceInfo.class);
for (VPlexDeviceInfo device : deviceInfoList) {
distributedDevicePathToClusterMap.put(device.getPath(), device.getCluster());
}
} else if (status == VPlexApiConstants.NOT_FOUND_STATUS) {
// return an empty list (at the end of this method) rather than an error
s_logger.info("VPLEX returned a 404 Not Found for this context, returning an empty list instead.");
} else {
throw VPlexApiException.exceptions.failedGettingDeviceStructure(String.valueOf(status));
}
s_logger.info("TIMER: getDistributedDevicePathToClusterMap took {}ms",
System.currentTimeMillis() - start);
return distributedDevicePathToClusterMap;
}
/**
* Calls the VPLEX CLI "drill-down" command for the given device name.
*
* @param deviceName a device name to check with the drill-down command
* @return the String drill-down command response from the VPLEX API
* @throws VPlexApiException if the device structure is incompatible with ViPR
*/
public String getDrillDownInfoForDevice(String deviceName) throws VPlexApiException {
ClientResponse response = null;
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_DRILL_DOWN);
s_logger.info("Drill-down command URI is {}", requestURI.toString());
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_R, deviceName);
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false);
s_logger.info("Drill-down command POST data is {}", postDataObject.toString());
response = _vplexApiClient.post(requestURI, postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Drill-down command response is {}", responseStr);
int status = response.getStatus();
response.close();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Drill-down command is completing asynchronously");
responseStr = _vplexApiClient.waitForCompletion(response);
s_logger.info("Task Response is {}", responseStr);
} else {
throw VPlexApiException.exceptions.failedToExecuteDrillDownCommand(deviceName, responseStr);
}
}
String customData = VPlexApiUtils.getCustomDataFromResponse(responseStr);
return customData;
}
/**
* Gets the backend storage volumes used by the passed device on the passed cluster.
*
* @param deviceName The device name.
* @param locality Whether the device is local or distributed.
* @param clusterName The cluster name.
* @param hasMirror Whether or not the device has a mirror.
*
* @return The list of VPlexStorageVolumeInfo representing the backend storage volumes.
*/
public List<VPlexStorageVolumeInfo> getBackendVolumesForDeviceOnCluster(String supportingDeviceName,
String locality, String clusterName, boolean hasMirror) {
// Get the storage volumes for the supporting device. Note that even
// though we pass a cluster name, for a distributed device, this will
// get the volumes on both clusters.
List<VPlexStorageVolumeInfo> svInfoList = getStorageVolumesForDevice(
supportingDeviceName, locality, clusterName, hasMirror);
// The storage volumes so returned do not specify the cluster, so we
// need to determine that for each volume and then eliminate those
// that are not on the passed cluster.
Iterator<VPlexStorageVolumeInfo> svInfoIter = svInfoList.iterator();
while (svInfoIter.hasNext()) {
VPlexStorageVolumeInfo svInfo = svInfoIter.next();
VPlexStorageVolumeInfo svInfoWithCluster = findStorageVolume(svInfo.getName());
s_logger.info("Cluster for {} is {}", svInfo.getWwn(), svInfoWithCluster.getClusterId());
if (!svInfoWithCluster.getClusterId().equals(clusterName)) {
svInfoIter.remove();
} else {
svInfo.setClusterId(clusterName);
}
}
return svInfoList;
}
/**
* Clears the local VPLEX REST API VPlexInitiatorInfo cache for all clusters.
*/
public synchronized void clearInitiatorCache() {
if (_vplexClusterInitiatorInfoCache != null) {
_vplexClusterInitiatorInfoCache.clear();
}
}
/**
* Clears the local VPLEX REST API VPlexInitiatorInfo cache for the given cluster.
*
* @param clusterName the cluster to clear initiator info for
*/
public synchronized void clearInitiatorCache(String clusterName) {
if (_vplexClusterInitiatorInfoCache != null) {
if (_vplexClusterInitiatorInfoCache.get(clusterName) != null) {
s_logger.info("clearing initiator cache for vplex cluster " + clusterName);
_vplexClusterInitiatorInfoCache.get(clusterName).clear();
}
}
}
}