/*
* 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.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jersey.api.client.ClientResponse;
/**
* VPlexApiConsistencyGroupManager provides methods for managing consistency groups.
*/
public class VPlexApiConsistencyGroupManager {
// Logger reference.
private static Logger s_logger = LoggerFactory.getLogger(VPlexApiConsistencyGroupManager.class);
// A reference to the API client.
private final VPlexApiClient _vplexApiClient;
/**
* Package protected constructor.
*
* @param client A reference to the API client.
*/
VPlexApiConsistencyGroupManager(VPlexApiClient client) {
_vplexApiClient = client;
}
/**
* Creates a consistency group with the passed name on the cluster with the
* passed name.
*
* @param cgName The name for the consistency group.
* @param clusterName The name of the cluster on which the group is created.
* @param isDistributed true if the CG will hold distributed volumes.
*
* @throws VPlexApiException When an error occurs creating the consistency
* group.
*/
void createConsistencyGroup(String cgName, String clusterName,
boolean isDistributed) throws VPlexApiException {
s_logger.info("Request to create consistency group {} on cluster {}", cgName,
clusterName);
// Find the cluster so we can get the cluster path.
VPlexClusterInfo clusterInfo = null;
VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager();
List<VPlexClusterInfo> clusterInfoList = discoveryMgr.getClusterInfoLite();
for (VPlexClusterInfo info : clusterInfoList) {
if (info.getName().equals(clusterName)) {
clusterInfo = info;
break;
}
}
// Error if not found.
if (clusterInfo == null) {
throw VPlexApiException.exceptions.failedToFindCluster(clusterName, "");
}
// Create the consistency group on the cluster.
ClientResponse response = null;
try {
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_CREATE_CG);
s_logger.info("Create consistency group URI is {}", requestURI.toString());
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_N, cgName);
argsMap.put(VPlexApiConstants.ARG_DASH_C, clusterInfo.getPath());
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false);
s_logger.info("Create consistency group POST data is {}",
postDataObject.toString());
response = _vplexApiClient.post(requestURI,
postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Create consistency group response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Consistency group creation completing asynchronously");
_vplexApiClient.waitForCompletion(response);
} else {
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions
.createConsistencyGroupFailureStatus(cgName,
String.valueOf(response.getStatus()), cause);
}
}
s_logger.info("Successfully created consistency group");
// Find the consistency group
VPlexConsistencyGroupInfo cgInfo = discoveryMgr.findConsistencyGroup(cgName,
Collections.singletonList(clusterInfo), false, true);
// Set the consistency group properties.
setAutoResumeAtLoser(cgInfo, true);
if (isDistributed) {
// For distributed the visibility and storage-at-clusters must
// be set to both clusters. The detach rule is set to winner
// at the cluster on which the consistency group is created.
s_logger.info("Is Distributed");
setConsistencyGroupVisibility(cgInfo, clusterInfoList);
setConsistencyGroupStorageClusters(cgInfo, clusterInfoList);
setDetachRuleWinner(cgInfo, clusterInfo);
} else {
setConsistencyGroupStorageClusters(cgInfo, Collections.singletonList(clusterInfo));
}
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedCreatingConsistencyGroup(cgName, e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Sets the visibility of the consistency group to the clusters in the
* passed list.
*
* @param cgInfo A reference to the consistency group info.
* @param clusterInfoList The list of clusters for which the CG should have
* visibility.
*
* @throws VPlexApiException When an error occurs setting the consistency
* group visibility.
*/
void setConsistencyGroupVisibility(VPlexConsistencyGroupInfo cgInfo,
List<VPlexClusterInfo> clusterInfoList) throws VPlexApiException {
ClientResponse response = null;
try {
// Build the request path.
int count = 0;
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(VPlexApiConstants.VPLEX_PATH);
pathBuilder.append(cgInfo.getPath());
pathBuilder.append("?");
pathBuilder.append(VPlexConsistencyGroupInfo.CGAttribute.VISIBILITY
.getAttributeName());
pathBuilder.append("=");
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
if (count > 0) {
pathBuilder.append(",");
}
pathBuilder.append(clusterInfo.getPath());
count++;
}
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(pathBuilder.toString()));
s_logger.info("Set CG visibility URI is {}", requestURI.toString());
response = _vplexApiClient.put(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Set CG visibility response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Set CG visibility is completing asynchronously");
_vplexApiClient.waitForCompletion(response);
} else {
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.setCGVisibilityFailureStatus(
cgInfo.getName(), String.valueOf(response.getStatus()), cause);
}
}
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedSettingCGVisibility(
cgInfo.getName(), e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Sets the "storage-at-clusters" attribute of the consistency group to the
* clusters in the passed list.
*
* @param cgInfo A reference to the consistency group info.
* @param clusterInfoList The list of clusters for which the CG has storage.
*
* @throws VPlexApiException When an error occurs setting the clusters
* at which the consistency group has storage.
*/
void setConsistencyGroupStorageClusters(VPlexConsistencyGroupInfo cgInfo,
List<VPlexClusterInfo> clusterInfoList) throws VPlexApiException {
// Build the request path.
ClientResponse response = null;
try {
int count = 0;
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(VPlexApiConstants.VPLEX_PATH);
pathBuilder.append(cgInfo.getPath());
pathBuilder.append("?");
pathBuilder.append(VPlexConsistencyGroupInfo.CGAttribute.STORAGE_AT_CLUSTER
.getAttributeName());
pathBuilder.append("=");
if (clusterInfoList.isEmpty()) {
pathBuilder.append("''");
} else {
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
if (count > 0) {
pathBuilder.append(",");
}
pathBuilder.append(clusterInfo.getPath());
count++;
}
}
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(pathBuilder.toString()));
s_logger.info("Set CG storage clusters URI is {}", requestURI.toString());
response = _vplexApiClient.put(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Set CG storage clusters response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Set CG storage clusters is completing asynchronously");
_vplexApiClient.waitForCompletion(response);
} else {
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.setCGStorageAtClustersFailureStatus(
cgInfo.getName(), String.valueOf(response.getStatus()), cause);
}
}
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedSettingCGStorageAtClusters(
cgInfo.getName(), e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Sets the detach rule for the passed consistency group to "winner", where
* the passed cluster is the winning cluster.
*
* @param cgInfo A reference to the consistency group info.
* @param clusterInfo The info for the cluster to be the winner.
*
* @throws VPlexApiException When an error occurs setting the detach rule to
* winner for the consistency group.
*/
void setDetachRuleWinner(VPlexConsistencyGroupInfo cgInfo, VPlexClusterInfo clusterInfo)
throws VPlexApiException {
ClientResponse response = null;
try {
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_CG_DETACH_RULE_WINNER);
s_logger.info("Set CG detach rule winner URI is {}", requestURI.toString());
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_C, clusterInfo.getPath());
argsMap.put(VPlexApiConstants.ARG_DASH_D, Integer.valueOf(VPlexApiConstants.DETACH_DELAY).toString());
argsMap.put(VPlexApiConstants.ARG_DASH_G, cgInfo.getPath());
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, true);
s_logger.info("Set CG detach rule winner is {}", postDataObject.toString());
response = _vplexApiClient.post(requestURI,
postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Set CG detach rule winner response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Set CG detach rule winner is completing asynchronously");
_vplexApiClient.waitForCompletion(response);
} else {
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.setDetachRuleWinnerFailureStatus(
cgInfo.getName(), String.valueOf(response.getStatus()), cause);
}
}
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedSettingDetachRuleWinner(
cgInfo.getName(), e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Sets the RP enabled tag on the consistency group to the clusters in the
* passed list.
*
* @param cgName The consistency group to update.
* @param clusterInfoList The list of clusters
*
* @throws VPlexApiException When an error occurs setting the consistency
* group visibility.
*/
void setConsistencyGroupRPEnabled(String cgName,
List<VPlexClusterInfo> clusterInfoList, boolean isRPEnabled) throws VPlexApiException {
// Find the consistency group
VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager();
VPlexConsistencyGroupInfo cgInfo = discoveryMgr.findConsistencyGroup(cgName,
clusterInfoList, true);
// Build the request path.
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(VPlexApiConstants.VPLEX_PATH);
pathBuilder.append(cgInfo.getPath());
pathBuilder.append("?");
pathBuilder.append(VPlexApiConstants.ATTRIBUTE_CG_RP_ENABLED);
pathBuilder.append("=");
pathBuilder.append(isRPEnabled);
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(pathBuilder.toString()));
s_logger.info("Set RP enabled URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.put(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Set RP enabled response is {}", responseStr);
int status = response.getStatus();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
if (status == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Set RP enabled is completing asynchronously");
_vplexApiClient.waitForCompletion(response);
response.close();
} else {
response.close();
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.setRPEnabledFailureStatus(
cgInfo.getName(), String.valueOf(response.getStatus()), cause);
}
}
}
/**
* Sets the detach rule for the passed consistency group to
* "no-automatic-winner".
*
* @param cgInfo A reference to the consistency group info.
* @param clusterInfo The info for the cluster to be the winner.
*
* @throws VPlexApiException When an error occurs setting the detach rule to
* winner for the consistency group.
*/
void setDetachRuleNoAutomaticWinner(VPlexConsistencyGroupInfo cgInfo)
throws VPlexApiException {
ClientResponse response = null;
try {
// Build the request path.
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_CG_DETACH_RULE_NO_AUTO_WINNER);
s_logger.info("Set CG detach rule no automatic winner URI is {}", requestURI.toString());
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_G, cgInfo.getPath());
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, true);
s_logger.info("Set CG detach rule no automatic winner is {}",
postDataObject.toString());
response = _vplexApiClient.post(requestURI,
postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Set CG detach rule no automatic winner response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Set CG detach rule no automatic winner is completing asynchronously");
_vplexApiClient.waitForCompletion(response);
} else {
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.setDetachRuleNoAutoWinnerFailureStatus(
cgInfo.getName(), String.valueOf(response.getStatus()), cause);
}
}
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedSettingDetachRuleNoAutoWinner(
cgInfo.getName(), e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Sets the auto-resume-at-loser flag under the advanced context
* of the requested Consistency Group.
*
* NOTE: as of VPLEX API version 5.5, this call is likely no
* longer necessary because the default value on new CG creation
* will be set to true by the VPLEX. Prior to 5.5, it is false
* by default. See also CTRL-10193.
*
* @param cgInfo The consistency group to update.
* @param autoResume the value to set auto-resume-at-loser to
*
* @throws VPlexApiException When an error occurs updating
* the consistency group visibility.
*/
void setAutoResumeAtLoser(VPlexConsistencyGroupInfo cgInfo, boolean autoResume) throws VPlexApiException {
// Build the request path.
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(VPlexApiConstants.VPLEX_PATH);
pathBuilder.append(cgInfo.getPath());
pathBuilder.append(VPlexApiConstants.URI_CGS_ADVANCED);
pathBuilder.append(VPlexApiConstants.QUESTION_MARK);
pathBuilder.append(VPlexApiConstants.ATTRIBUTE_CG_AUTO_RESUME);
pathBuilder.append(VPlexApiConstants.EQUALS);
pathBuilder.append(autoResume);
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(pathBuilder.toString()));
s_logger.info("Set auto-resume-at-loser URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.put(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Set auto-resume-at-loser response is {}", responseStr);
int status = response.getStatus();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
if (status == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Set auto-resume-at-loser is completing asynchronously");
_vplexApiClient.waitForCompletion(response);
response.close();
} else {
response.close();
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.setCGAutoResumeFailureStatus(
cgInfo.getName(), String.valueOf(response.getStatus()), cause);
}
}
}
/**
* Adds the volumes with the passed names to the consistency group with the
* passed name.
*
* @param cgName The name of the consistency group to which the volumes are
* added.
* @param virtualVolumeNames The names of the virtual volumes to be added to
* the consistency group.
*
* @throws VPlexApiException When an error occurs adding the volumes to the
* consistency group.
*/
void addVolumesToConsistencyGroup(String cgName,
List<String> virtualVolumeNames) throws VPlexApiException {
s_logger.info("Request to add volumes {} to a consistency group {}",
virtualVolumeNames, cgName);
// Find the virtual volumes with the passed names.
VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager();
List<VPlexClusterInfo> clusterInfoList = discoveryMgr.getClusterInfoLite();
Map<String, List<VPlexVirtualVolumeInfo>> clusterToVirtualVolumes = new HashMap<String, List<VPlexVirtualVolumeInfo>>();
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
List<VPlexVirtualVolumeInfo> clusterVolumeInfoList = discoveryMgr.getVirtualVolumesForCluster(clusterInfo.getName());
clusterToVirtualVolumes.put(clusterInfo.getName(), clusterVolumeInfoList);
}
List<VPlexVirtualVolumeInfo> virtualVolumeInfoList = new ArrayList<VPlexVirtualVolumeInfo>();
List<String> notFoundVirtualVolumeNames = new ArrayList<String>();
for (String virtualVolumeName : virtualVolumeNames) {
s_logger.info("Find virtual volume {}", virtualVolumeName);
VPlexVirtualVolumeInfo virtualVolumeInfo = null;
for (String clusterId : clusterToVirtualVolumes.keySet()) {
List<VPlexVirtualVolumeInfo> clusterVolumeInfoList = clusterToVirtualVolumes.get(clusterId);
for (VPlexVirtualVolumeInfo volumeInfo : clusterVolumeInfoList) {
s_logger.info("Virtual volume Info: {}", volumeInfo.toString());
if (volumeInfo.getName().equals(virtualVolumeName)) {
s_logger.info("Found virtual volume {}", volumeInfo.getName());
virtualVolumeInfo = volumeInfo;
break;
}
}
if (virtualVolumeInfo != null) {
break;
}
}
if (virtualVolumeInfo == null) {
notFoundVirtualVolumeNames.add(virtualVolumeName);
} else {
virtualVolumeInfoList.add(virtualVolumeInfo);
}
}
if (!notFoundVirtualVolumeNames.isEmpty()) {
throw VPlexApiException.exceptions.cantFindRequestedVolume(notFoundVirtualVolumeNames.toString());
}
// Find the consistency group
VPlexConsistencyGroupInfo cgInfo = discoveryMgr.findConsistencyGroup(cgName,
clusterInfoList, false);
// Add the virtual volumes to the consistency group.
ClientResponse response = null;
try {
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_ADD_VOLUMES_TO_CG);
s_logger.info("Add volumes to consistency group URI is {}",
requestURI.toString());
StringBuilder argBuilder = new StringBuilder();
for (VPlexVirtualVolumeInfo virtualVolumeInfo : virtualVolumeInfoList) {
if (argBuilder.length() != 0) {
argBuilder.append(",");
}
argBuilder.append(virtualVolumeInfo.getPath());
}
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_V, argBuilder.toString());
argsMap.put(VPlexApiConstants.ARG_DASH_G, cgInfo.getPath());
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, false);
s_logger.info("Add volumes to consistency group POST data is {}",
postDataObject.toString());
response = _vplexApiClient.post(requestURI,
postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Add volumes to consistency group response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
// We've seen cases where we send the request to add volumes to the CG over to
// VPLEX and it completes the operation successfully but never returns with
// a response. Specifically, we see this if we strart 2 concurrent Metropoint
// volume create orders at the same time.
// Ideally, we want to work with the VPLEX team to debug and figure out why
// this is happening and fix it, but we need to get a fix into a patch in the
// short term. The short term solution is to query the VPLEX for the existence
// of the volumes in the CG after VPLEX returns with the async status response.
// We'll check again after waiting for the asynchronous request to time out
if (!areVolumesInCG(cgName, clusterInfoList, virtualVolumeNames)) {
s_logger.info("Add volumes to consistency group completing asynchronously");
try {
_vplexApiClient.waitForCompletion(response);
} catch (VPlexApiException ex) {
// check for the volumes in the CG once more before failing
if (!areVolumesInCG(cgName, clusterInfoList, virtualVolumeNames)) {
throw ex;
}
}
}
} else {
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.addVolumesToCGFailureStatus(
cgInfo.getName(), String.valueOf(response.getStatus()), cause);
}
}
s_logger.info("Successfully added volumes to consistency group");
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedAddingVolumesToCG(cgInfo.getName(), e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* checks for the presence of a list of virtual volumes in a CG
*
* @param cgName
* @param clusterInfoList
* @param virtualVolumeNames
* @return true if all volumes are in the CG
*/
private boolean areVolumesInCG(String cgName, List<VPlexClusterInfo> clusterInfoList, List<String> virtualVolumeNames) {
VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager();
VPlexConsistencyGroupInfo cgInfo = discoveryMgr.findConsistencyGroup(cgName, clusterInfoList, true);
for (String vvolName : virtualVolumeNames) {
if (!cgInfo.getVirtualVolumes().contains(vvolName)) {
return false;
}
}
return true;
}
/**
* Deletes the consistency group with the passed name.
*
* @param cgName The name of the consistency group to be deleted.
*
* @throws VPlexApiException When an error occurs deleting the consistency group.
*/
void deleteConsistencyGroup(String cgName) throws VPlexApiException {
s_logger.info("Request to delete consistency group {}", cgName);
// Find the consistency group.
VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager();
List<VPlexClusterInfo> clusterInfoList = discoveryMgr.getClusterInfoLite();
VPlexConsistencyGroupInfo cgInfo = discoveryMgr.findConsistencyGroup(cgName,
clusterInfoList, false);
// COP-17138 If the consistency group still has virtual volumes in the VPLEX, don't delete it in the VPLEX
discoveryMgr.updateConsistencyGroupInfo(cgInfo);
if (cgInfo.getVirtualVolumes().isEmpty()) {
deleteConsistencyGroup(cgInfo);
} else {
s_logger.info("The consistency group {} still has virtual volumes in VPLEX, not deleting it in the VPLEX", cgName);
}
}
/**
* Deletes the passed consistency group.
*
* @param cgInfo A reference to the consistency group info.
*
* @throws VPlexApiException When an error occurs deleting the consistency
* group.
*/
private void deleteConsistencyGroup(VPlexConsistencyGroupInfo cgInfo)
throws VPlexApiException {
// Delete the consistency group.
ClientResponse response = null;
try {
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_DELETE_CG);
s_logger.info("Delete consistency group URI is {}", requestURI.toString());
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_G, cgInfo.getPath());
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, true);
s_logger.info("Delete consistency group POST data is {}",
postDataObject.toString());
response = _vplexApiClient.post(requestURI,
postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Delete consistency group response is {}", responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Consistency group deletion completing asynchronously");
_vplexApiClient.waitForCompletion(response);
} else {
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.deleteCGFailureStatus(
cgInfo.getName(), String.valueOf(response.getStatus()), cause);
}
}
s_logger.info("Successfully deleted consistency group");
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedDeleteCG(cgInfo.getName(), e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Removes the volumes with the passed names from the consistency group with
* the passed name. If the removal of the volumes results in an empty group,
* delete the consistency group if the passed flag so indicates.
*
* @param virtualVolumeNames The names of the virtual volumes to be removed
* from the consistency group.
* @param cgName The name of the consistency group from which the volume is
* removed.
* @param deleteCGWhenEmpty true to delete the consistency group if the
* group is empty after removing the volumes, false otherwise.
*
* @return true if the consistency group was deleted, false otherwise.
*
* @throws VPlexApiException When an error occurs removing the volumes from
* the consistency group.
*/
boolean removeVolumesFromConsistencyGroup(List<String> virtualVolumeNames,
String cgName, boolean deleteCGWhenEmpty) throws VPlexApiException {
s_logger.info("Request to remove volumes {} from consistency group {}",
virtualVolumeNames.toString(), cgName);
boolean cgDeleted = false;
// Find the virtual volumes with the passed names.
VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager();
List<VPlexClusterInfo> clusterInfoList = discoveryMgr.getClusterInfoLite();
List<VPlexVirtualVolumeInfo> virtualVolumeInfoList = new ArrayList<VPlexVirtualVolumeInfo>();
for (String virtualVolumeName : virtualVolumeNames) {
VPlexVirtualVolumeInfo virtualVolumeInfo = null;
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
virtualVolumeInfo = discoveryMgr.findVirtualVolume(clusterInfo.getName(),
virtualVolumeName, false);
if (virtualVolumeInfo != null) {
break;
}
}
if (virtualVolumeInfo == null) {
throw VPlexApiException.exceptions.cantFindRequestedVolume(virtualVolumeName);
}
virtualVolumeInfoList.add(virtualVolumeInfo);
}
// Find the consistency group.
VPlexConsistencyGroupInfo cgInfo = discoveryMgr.findConsistencyGroup(cgName,
clusterInfoList, false);
// Remove the virtual volume from the consistency group.
ClientResponse response = null;
try {
URI requestURI = _vplexApiClient.getBaseURI().resolve(
VPlexApiConstants.URI_REMOVE_VOLUMES_FROM_CG);
s_logger.info("Remove volumes from consistency group URI is {}",
requestURI.toString());
StringBuilder argBuilder = new StringBuilder();
for (VPlexVirtualVolumeInfo virtualVolumeInfo : virtualVolumeInfoList) {
if (argBuilder.length() != 0) {
argBuilder.append(",");
}
argBuilder.append(virtualVolumeInfo.getPath());
}
Map<String, String> argsMap = new HashMap<String, String>();
argsMap.put(VPlexApiConstants.ARG_DASH_V, argBuilder.toString());
argsMap.put(VPlexApiConstants.ARG_DASH_G, cgInfo.getPath());
JSONObject postDataObject = VPlexApiUtils.createPostData(argsMap, true);
s_logger.info("Remove volumes from consistency group POST data is {}",
postDataObject.toString());
response = _vplexApiClient.post(requestURI,
postDataObject.toString());
String responseStr = response.getEntity(String.class);
s_logger.info("Remove volumes from consistency group response is {}",
responseStr);
if (response.getStatus() != VPlexApiConstants.SUCCESS_STATUS) {
if (response.getStatus() == VPlexApiConstants.ASYNC_STATUS) {
s_logger
.info("Remove volumes from consistency group completing asynchronously");
_vplexApiClient.waitForCompletion(response);
} else {
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.removeVolumesFromCGFailureStatus(
cgInfo.getName(), String.valueOf(response.getStatus()), cause);
}
}
s_logger.info("Successfully removed volumes from consistency group");
// If the flag so indicates
if (deleteCGWhenEmpty) {
discoveryMgr.updateConsistencyGroupInfo(cgInfo);
if (cgInfo.getVirtualVolumes().isEmpty()) {
s_logger.info("Deleting empty consistency group {}", cgName);
try {
deleteConsistencyGroup(cgInfo);
cgDeleted = true;
} catch (Exception e) {
s_logger
.error("Exception deleting consistency group {}:{}", cgName, e.getMessage());
}
}
}
} catch (VPlexApiException vae) {
throw vae;
} catch (Exception e) {
throw VPlexApiException.exceptions.failedRemovingVolumesFromCG(cgInfo.getName(), e);
} finally {
if (response != null) {
response.close();
}
}
return cgDeleted;
}
/**
* Sets the read-only flag on the consistency group to the clusters in the
* passed list. Note: This flag only supported in VPlex 5.5 and beyond, and it
* requires a patch (not yet released as of 2016May26) to work on Consistency Groups
* with distributed virtual volumes. It throws vplexFirmwareUpdateNeeded
* if the firmware does not fully support thre read-only operation.
*
* @param cgName The consistency group to update.
* @param clusterInfoList The list of clusters
*
* @throws VPlexApiException When an error occurs setting the consistency
* group visibility.
*/
void setConsistencyGroupReadOnly(String cgName,
List<VPlexClusterInfo> clusterInfoList, boolean isReadOnly) throws VPlexApiException {
// Find the consistency group
VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager();
VPlexConsistencyGroupInfo cgInfo = discoveryMgr.findConsistencyGroup(cgName,
clusterInfoList, true);
// Build the request path.
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(VPlexApiConstants.VPLEX_PATH);
pathBuilder.append(cgInfo.getPath());
pathBuilder.append("?");
pathBuilder.append(VPlexApiConstants.ATTRIBUTE_CG_READ_ONLY);
pathBuilder.append("=");
pathBuilder.append(isReadOnly);
URI requestURI = _vplexApiClient.getBaseURI().resolve(
URI.create(pathBuilder.toString()));
s_logger.info("Set read-only in CG URI is {}", requestURI.toString());
ClientResponse response = _vplexApiClient.put(requestURI);
String responseStr = response.getEntity(String.class);
s_logger.info("Set read-only response is {}", responseStr);
int status = response.getStatus();
if (status != VPlexApiConstants.SUCCESS_STATUS) {
if (status == VPlexApiConstants.ASYNC_STATUS) {
s_logger.info("Set read-only in CG is completing asynchronously");
_vplexApiClient.waitForCompletion(response);
response.close();
} else {
response.close();
if (responseStr.contains(VPlexApiConstants.CG_READ_ONLY_INVALID_ATTRIBUTE)
|| responseStr.contains(VPlexApiConstants.CG_CANNOT_MAKE_READ_ONLY)) {
throw VPlexApiException.exceptions
.vplexFirmwareUpdateNeeded(VPlexApiConstants.CG_READ_ONLY_ATTRIBUTE_NOT_SUPPORTED);
}
String cause = VPlexApiUtils.getCauseOfFailureFromResponse(responseStr);
throw VPlexApiException.exceptions.setConsistencyGroupReadOnlyFailureStatus(
cgInfo.getName(), String.valueOf(response.getStatus()), cause);
}
}
}
}