/*
* Copyright (c) 2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource;
import static com.emc.storageos.api.mapper.TaskMapper.toCompletedTask;
import static com.emc.storageos.api.mapper.TaskMapper.toTask;
import static com.emc.storageos.db.client.constraint.ContainmentConstraint.Factory.getBlockSnapshotByConsistencyGroup;
import static java.text.MessageFormat.format;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.api.mapper.TaskMapper;
import com.emc.storageos.api.service.impl.placement.StorageScheduler;
import com.emc.storageos.api.service.impl.placement.VpoolUse;
import com.emc.storageos.api.service.impl.resource.fullcopy.BlockFullCopyManager;
import com.emc.storageos.api.service.impl.resource.utils.VirtualPoolChangeAnalyzer;
import com.emc.storageos.blockorchestrationcontroller.BlockOrchestrationController;
import com.emc.storageos.blockorchestrationcontroller.VolumeDescriptor;
import com.emc.storageos.db.client.constraint.AlternateIdConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.BlockConsistencyGroup;
import com.emc.storageos.db.client.model.BlockSnapshot;
import com.emc.storageos.db.client.model.DiscoveredDataObject;
import com.emc.storageos.db.client.model.DiscoveredDataObject.Type;
import com.emc.storageos.db.client.model.Operation;
import com.emc.storageos.db.client.model.Project;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.model.VirtualArray;
import com.emc.storageos.db.client.model.VirtualPool;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.client.model.VolumeGroup;
import com.emc.storageos.db.client.util.CustomQueryUtility;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.emc.storageos.db.client.util.SizeUtil;
import com.emc.storageos.model.ResourceOperationTypeEnum;
import com.emc.storageos.model.TaskList;
import com.emc.storageos.model.TaskResourceRep;
import com.emc.storageos.model.application.VolumeGroupUpdateParam.VolumeGroupVolumeList;
import com.emc.storageos.model.block.VolumeCreate;
import com.emc.storageos.model.systems.StorageSystemConnectivityList;
import com.emc.storageos.model.vpool.VirtualPoolChangeList;
import com.emc.storageos.model.vpool.VirtualPoolChangeOperationEnum;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.svcs.errorhandling.resources.InternalException;
import com.emc.storageos.util.InvokeTestFailure;
import com.emc.storageos.volumecontroller.ApplicationAddVolumeList;
import com.emc.storageos.volumecontroller.BlockController;
import com.emc.storageos.volumecontroller.ControllerException;
import com.emc.storageos.volumecontroller.Recommendation;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper;
/**
* Block Service subtask (parts of larger operations) default implementation.
*/
public class DefaultBlockServiceApiImpl extends AbstractBlockServiceApiImpl<StorageScheduler> {
private static final Logger _log = LoggerFactory.getLogger(DefaultBlockServiceApiImpl.class);
private static final String REGEX_XIV = "^[-\\w._~]+";
public DefaultBlockServiceApiImpl() {
super(null);
}
private List<VolumeDescriptor> prepareVolumeDescriptors(List<Volume> volumes, VirtualPoolCapabilityValuesWrapper cosCapabilities) {
// Build up a list of VolumeDescriptors based on the volumes
final List<VolumeDescriptor> volumeDescriptors = new ArrayList<VolumeDescriptor>();
for (Volume volume : volumes) {
VolumeDescriptor desc = new VolumeDescriptor(VolumeDescriptor.Type.BLOCK_DATA,
volume.getStorageController(), volume.getId(),
volume.getPool(), volume.getConsistencyGroup(), cosCapabilities);
volumeDescriptors.add(desc);
}
return volumeDescriptors;
}
@Override
public TaskList createVolumes(VolumeCreate param, Project project, VirtualArray neighborhood,
VirtualPool cos, Map<VpoolUse, List<Recommendation>> recommendationMap, TaskList taskList,
String task, VirtualPoolCapabilityValuesWrapper cosCapabilities) throws InternalException {
Long size = SizeUtil.translateSize(param.getSize());
List<VolumeDescriptor> existingDescriptors = new ArrayList<VolumeDescriptor>();
List<VolumeDescriptor> volumeDescriptors = createVolumesAndDescriptors(
existingDescriptors, param.getName(), size,
project, neighborhood, cos, recommendationMap.get(VpoolUse.ROOT), taskList, task, cosCapabilities);
List<Volume> preparedVolumes = getPreparedVolumes(volumeDescriptors);
//Check for special characters in volume names
checkVolumeNames(preparedVolumes.get(0));
final BlockOrchestrationController controller = getController(BlockOrchestrationController.class,
BlockOrchestrationController.BLOCK_ORCHESTRATION_DEVICE);
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_011);
try {
// Execute the volume creations requests
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_012);
controller.createVolumes(volumeDescriptors, task);
} catch (InternalException e) {
_log.error("Controller error when creating volumes", e);
failVolumeCreateRequest(task, taskList, preparedVolumes, e.getMessage());
throw e;
} catch (Exception e) {
_log.error("Controller error when creating volumes", e);
failVolumeCreateRequest(task, taskList, preparedVolumes, e.getMessage());
throw e;
}
return taskList;
}
/**
* Method to validate volume names.
* @param volume Volume Name
*/
private void checkVolumeNames(Volume volume) {
URI storageSystemUri = volume.getStorageController();
if(null != storageSystemUri){
StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, storageSystemUri);
//A check for XIV array volume name. XIV volume name supports only four special character .~-_
if(storageSystem.deviceIsType(Type.ibmxiv)){
if(volume.getLabel().matches(REGEX_XIV)){
return;
}else{
throw APIException.badRequests.invalidVolumeName(volume.getLabel());
}
}
}
}
@Override
public List<VolumeDescriptor> createVolumesAndDescriptors(List<VolumeDescriptor> descriptors, String volumeLabel, Long size,
Project project,
VirtualArray varray, VirtualPool vpool, List<Recommendation> recommendations, TaskList taskList, String task,
VirtualPoolCapabilityValuesWrapper vpoolCapabilities) {
// Prepare the Bourne Volumes to be created and associated
// with the actual storage system volumes created. Also create
// a BlockTaskList containing the list of task resources to be
// returned for the purpose of monitoring the volume creation
// operation for each volume to be created.
int volumeCounter = 0;
List<Volume> preparedVolumes = new ArrayList<Volume>();
final BlockConsistencyGroup consistencyGroup = vpoolCapabilities.getBlockConsistencyGroup() == null ? null : _dbClient
.queryObject(BlockConsistencyGroup.class, vpoolCapabilities.getBlockConsistencyGroup());
// Prepare the volumes
_scheduler.prepareRecommendedVolumes(size, task, taskList, project,
varray, vpool, vpoolCapabilities.getResourceCount(), recommendations,
consistencyGroup, volumeCounter, volumeLabel, preparedVolumes, vpoolCapabilities, false);
// Prepare the volume descriptors based on the recommendations
final List<VolumeDescriptor> volumeDescriptors = prepareVolumeDescriptors(preparedVolumes, vpoolCapabilities);
// Log volume descriptor information
logVolumeDescriptorPrecreateInfo(volumeDescriptors, task);
return volumeDescriptors;
}
/**
* Retrieves the preparedVolumes from the volume descriptors.
*
* @param descriptors
* @return List<Volume>
*/
private List<Volume> getPreparedVolumes(List<VolumeDescriptor> descriptors) {
List<URI> volumeURIs = VolumeDescriptor.getVolumeURIs(descriptors);
@SuppressWarnings("deprecation")
List<Volume> volumes = _dbClient.queryObject(Volume.class, volumeURIs);
return volumes;
}
private void failVolumeCreateRequest(String task, TaskList taskList, List<Volume> preparedVolumes, String errorMsg) {
String errorMessage = String.format("Controller error: %s", errorMsg);
for (TaskResourceRep volumeTask : taskList.getTaskList()) {
volumeTask.setState(Operation.Status.error.name());
volumeTask.setMessage(errorMessage);
Operation statusUpdate = new Operation(Operation.Status.error.name(), errorMessage);
_dbClient.updateTaskOpStatus(Volume.class, volumeTask.getResource()
.getId(), task, statusUpdate);
}
for (Volume volume : preparedVolumes) {
volume.setInactive(true);
_dbClient.persistObject(volume);
}
}
/**
* {@inheritDoc}
*
* @throws InternalException
*/
@Override
public void deleteVolumes(final URI systemURI, final List<URI> volumeURIs,
final String deletionType, final String task) throws InternalException {
_log.info("Request to delete {} volume(s)", volumeURIs.size());
super.deleteVolumes(systemURI, volumeURIs, deletionType, task);
}
/**
* {@inheritDoc}
*/
@Override
protected void cleanupForViPROnlyDelete(List<VolumeDescriptor> volumeDescriptors) {
// Call super first.
super.cleanupForViPROnlyDelete(volumeDescriptors);
// Clean up the relationship between volumes that are full
// copies and and their source volumes.
BlockFullCopyManager.cleanUpFullCopyAssociations(volumeDescriptors, _dbClient);
}
// Connectivity for a VNX/VMAX array is determined by the various protection systems.
// This method calls all the known protection systems to find out which are covering
// this StorageArray.
@Override
public StorageSystemConnectivityList getStorageSystemConnectivity(StorageSystem storageSystem) {
Map<String, AbstractBlockServiceApiImpl> apiMap = AbstractBlockServiceApiImpl.getProtectionImplementations();
StorageSystemConnectivityList result = new StorageSystemConnectivityList();
for (AbstractBlockServiceApiImpl impl : apiMap.values()) {
if (impl == this) {
continue; // no infinite recursion
}
StorageSystemConnectivityList list = impl.getStorageSystemConnectivity(storageSystem);
result.getConnections().addAll(list.getConnections());
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public VirtualPoolChangeList getVirtualPoolForVirtualPoolChange(Volume volume) {
// VirtualPool change is only potentially supported for VNX and VMAX block.
// So, in this case we throw a bad request exception.
URI volumeSystemURI = volume.getStorageController();
StorageSystem volumeSystem = _dbClient.queryObject(StorageSystem.class,
volumeSystemURI);
String systemType = volumeSystem.getSystemType();
if (!DiscoveredDataObject.Type.vmax.name().equals(systemType)
&& !DiscoveredDataObject.Type.vnxblock.name().equals(systemType)
&& !DiscoveredDataObject.Type.hds.name().equals(systemType)
&& !DiscoveredDataObject.Type.xtremio.name().equals(systemType)
&& !DiscoveredDataObject.Type.ibmxiv.name().equals(systemType)
&& !DiscoveredDataObject.Type.unity.name().equals(systemType)) {
throw APIException.badRequests.changesNotSupportedFor("VirtualPool",
format("volumes on storage systems of type {0}", systemType));
}
return getVirtualPoolChangeListForVolume(volume);
}
/**
* {@inheritDoc}
*/
@Override
protected List<VirtualPoolChangeOperationEnum> getVirtualPoolChangeAllowedOperations(Volume volume, VirtualPool volumeVirtualPool,
VirtualPool newVirtualPool, StringBuffer notSuppReasonBuff) {
List<VirtualPoolChangeOperationEnum> allowedOperations = new ArrayList<VirtualPoolChangeOperationEnum>();
if (VirtualPool.vPoolSpecifiesHighAvailability(newVirtualPool) &&
VirtualPoolChangeAnalyzer.isVPlexImport(volume, volumeVirtualPool, newVirtualPool, notSuppReasonBuff) &&
VirtualPoolChangeAnalyzer.doesVplexVpoolContainVolumeStoragePool(volume, newVirtualPool, notSuppReasonBuff)) {
allowedOperations.add(VirtualPoolChangeOperationEnum.NON_VPLEX_TO_VPLEX);
}
if (VirtualPool.vPoolSpecifiesProtection(newVirtualPool) &&
VirtualPoolChangeAnalyzer.isSupportedAddRPProtectionVirtualPoolChange(volume,
volumeVirtualPool, newVirtualPool, _dbClient, notSuppReasonBuff)) {
allowedOperations.add(VirtualPoolChangeOperationEnum.RP_PROTECTED);
}
if (VirtualPool.vPoolSpecifiesSRDF(newVirtualPool) &&
VirtualPoolChangeAnalyzer.isSupportedSRDFVolumeVirtualPoolChange(volume,
volumeVirtualPool, newVirtualPool, _dbClient, notSuppReasonBuff)) {
allowedOperations.add(VirtualPoolChangeOperationEnum.SRDF_PROTECED);
}
if (VirtualPool.vPoolSpecifiesMirrors(newVirtualPool, _dbClient)
&&
VirtualPoolChangeAnalyzer.isSupportedAddMirrorsVirtualPoolChange(volume, volumeVirtualPool, newVirtualPool, _dbClient,
notSuppReasonBuff)) {
allowedOperations.add(VirtualPoolChangeOperationEnum.ADD_MIRRORS);
}
return allowedOperations;
}
/**
* {@inheritDoc}
*/
@Override
public TaskResourceRep deleteConsistencyGroup(StorageSystem device,
BlockConsistencyGroup consistencyGroup, String task) throws ControllerException {
Operation op = _dbClient.createTaskOpStatus(BlockConsistencyGroup.class, consistencyGroup.getId(),
task, ResourceOperationTypeEnum.DELETE_CONSISTENCY_GROUP);
BlockController controller = getController(BlockController.class,
device.getSystemType());
controller.deleteConsistencyGroup(device.getId(), consistencyGroup.getId(), Boolean.TRUE, task);
return toTask(consistencyGroup, task, op);
}
/**
* {@inheritDoc}
*/
@Override
public TaskResourceRep updateConsistencyGroup(StorageSystem device,
List<Volume> cgVolumes, BlockConsistencyGroup consistencyGroup,
List<URI> addVolumesList, List<URI> removeVolumesList, String task)
throws ControllerException {
Operation op = _dbClient.createTaskOpStatus(BlockConsistencyGroup.class,
consistencyGroup.getId(), task,
ResourceOperationTypeEnum.UPDATE_CONSISTENCY_GROUP);
if (!device.getSystemType().equals(DiscoveredDataObject.Type.scaleio.name())) {
BlockController controller = getController(BlockController.class,
device.getSystemType());
controller.updateConsistencyGroup(device.getId(), consistencyGroup.getId(),
addVolumesList, removeVolumesList, task);
return toTask(consistencyGroup, task, op);
} else {
// ScaleIO does not have explicit CGs, so we can just update the database and complete
Iterator<Volume> addVolumeItr = _dbClient.queryIterativeObjects(Volume.class, addVolumesList);
List<Volume> addVolumes = new ArrayList<Volume>();
while (addVolumeItr.hasNext()) {
Volume volume = addVolumeItr.next();
volume.setConsistencyGroup(consistencyGroup.getId());
addVolumes.add(volume);
}
Iterator<Volume> removeVolumeItr = _dbClient.queryIterativeObjects(Volume.class, removeVolumesList);
List<Volume> removeVolumes = new ArrayList<Volume>();
while (removeVolumeItr.hasNext()) {
Volume volume = removeVolumeItr.next();
volume.setConsistencyGroup(consistencyGroup.getId());
removeVolumes.add(volume);
}
_dbClient.updateObject(addVolumes);
_dbClient.updateObject(removeVolumes);
return toCompletedTask(consistencyGroup, task, op);
}
}
/**
* {@inheritDoc}
*/
@Override
public List<VolumeDescriptor> getDescriptorsForVolumesToBeDeleted(URI systemURI,
List<URI> volumeURIs, String deletionType) {
List<VolumeDescriptor> volumeDescriptors = new ArrayList<VolumeDescriptor>();
for (URI volumeURI : volumeURIs) {
VolumeDescriptor desc = new VolumeDescriptor(VolumeDescriptor.Type.BLOCK_DATA,
systemURI, volumeURI, null, null);
volumeDescriptors.add(desc);
}
return volumeDescriptors;
}
/**
* {@inheritDoc}
*
* @throws ControllerException
*/
@Override
public TaskResourceRep establishVolumeAndSnapshotGroupRelation(
StorageSystem storageSystem, Volume sourceVolume,
BlockSnapshot snapshot, String taskId) throws ControllerException {
_log.info("START establish Volume and Snapshot group relation");
// Create the task on the block snapshot
Operation op = _dbClient.createTaskOpStatus(BlockSnapshot.class, snapshot.getId(),
taskId, ResourceOperationTypeEnum.ESTABLISH_VOLUME_SNAPSHOT);
snapshot.getOpStatus().put(taskId, op);
try {
BlockController controller = getController(BlockController.class,
storageSystem.getSystemType());
controller.establishVolumeAndSnapshotGroupRelation(storageSystem.getId(),
sourceVolume.getId(), snapshot.getId(), taskId);
} catch (ControllerException e) {
String errorMsg = String.format(
"Failed to establish group relation between volume group and snapshot group."
+ "Source volume: %s, Snapshot: %s",
sourceVolume.getId(), snapshot.getId());
_log.error(errorMsg, e);
_dbClient.error(BlockSnapshot.class, snapshot.getId(), taskId, e);
}
return toTask(snapshot, taskId, op);
}
/**
* {@inheritDoc}
*/
@Override
public void updateVolumesInVolumeGroup(VolumeGroupVolumeList addVolumes,
List<Volume> removeVolumes,
URI volumeGroupId,
String taskId) {
VolumeGroup volumeGroup = _dbClient.queryObject(VolumeGroup.class, volumeGroupId);
ApplicationAddVolumeList addVolumeList = null;
if (addVolumes != null && addVolumes.getVolumes() != null && !addVolumes.getVolumes().isEmpty()) {
addVolumeList = addVolumesToApplication(addVolumes, volumeGroup, taskId);
}
if (removeVolumes != null && !removeVolumes.isEmpty()) {
removeVolumesFromApplication(removeVolumes, volumeGroup, taskId);
}
// call controller to handle non application ready CG volumes
if ((addVolumeList != null && !addVolumeList.getVolumes().isEmpty())) {
List<URI> vols = addVolumeList.getVolumes();
Volume firstVolume = _dbClient.queryObject(Volume.class, vols.get(0));
URI systemURI = firstVolume.getStorageController();
StorageSystem system = _dbClient.queryObject(StorageSystem.class, systemURI);
BlockController controller = getController(BlockController.class, system.getSystemType());
controller.updateApplication(systemURI, addVolumeList, volumeGroup.getId(), taskId);
} else {
// No need to call to controller. update the application task
Operation op = volumeGroup.getOpStatus().get(taskId);
op.ready();
volumeGroup.getOpStatus().updateTaskStatus(taskId, op);
_dbClient.updateObject(volumeGroup);
}
}
/**
* Update volumes with volumeGroup Id, if the volumes are application ready
* (non VNX, or VNX volumes not in a real replication group)
*
* @param volumesList
* The add volume list
* @param application
* The application that the volumes are added to
* @param taskId
* @return ApplicationVolumeList The volumes that are not application ready (in real VNX CG with array replication
* group)
*/
private ApplicationAddVolumeList addVolumesToApplication(VolumeGroupVolumeList volumeList, VolumeGroup application, String taskId) {
ApplicationAddVolumeList addVolumeList = new ApplicationAddVolumeList();
Map<URI, List<URI>> addCGVolsMap = new HashMap<URI, List<URI>>();
String newRGName = volumeList.getReplicationGroupName();
for (URI voluri : volumeList.getVolumes()) {
Volume volume = _dbClient.queryObject(Volume.class, voluri);
if (volume == null || volume.getInactive()) {
_log.info(String.format("The volume %s does not exist or has been deleted", voluri));
continue;
}
URI cgUri = volume.getConsistencyGroup();
if (!NullColumnValueGetter.isNullURI(cgUri)) {
List<URI> vols = addCGVolsMap.get(cgUri);
if (vols == null) {
vols = new ArrayList<URI>();
}
vols.add(voluri);
addCGVolsMap.put(cgUri, vols);
} else {
// The volume is not in CG
throw APIException.badRequests.volumeGroupCantBeUpdated(application.getLabel(),
String.format("The volume %s is not in a consistency group", volume.getLabel()));
}
String rgName = volume.getReplicationGroupInstance();
if (NullColumnValueGetter.isNotNullValue(rgName) && !rgName.equals(newRGName)) {
throw APIException.badRequests.volumeGroupCantBeUpdated(application.getLabel(),
String.format("The volume %s is already in an array replication group, only the existing group name is allowed.",
volume.getLabel()));
}
}
Set<URI> appReadyCGUris = new HashSet<URI>();
Set<Volume> appReadyCGVols = new HashSet<Volume>();
Set<URI> nonAppReadyCGVolUris = new HashSet<URI>();
// validate input volumes first, then batch processing, to avoid partial success
for (Map.Entry<URI, List<URI>> entry : addCGVolsMap.entrySet()) {
URI cgUri = entry.getKey();
List<URI> cgVolsToAdd = entry.getValue();
BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, cgUri);
List<Volume> cgVolumes = getActiveCGVolumes(cg);
Set<URI> cgVolumeURIs = new HashSet<URI>();
for (Volume cgVol : cgVolumes) {
cgVolumeURIs.add(cgVol.getId());
}
Volume firstVolume = _dbClient.queryObject(Volume.class, cgVolsToAdd.get(0));
// Check if all CG volumes are adding into the application
if (!cgVolumeURIs.containsAll(cgVolsToAdd) || cgVolsToAdd.size() != cgVolumeURIs.size()) {
throw APIException.badRequests.volumeCantBeAddedToVolumeGroup(firstVolume.getLabel(),
"not all volumes in consistency group are in the add volume list");
}
if (ControllerUtils.isVnxVolume(firstVolume, _dbClient) && !ControllerUtils.isNotInRealVNXRG(firstVolume, _dbClient)) {
// VNX CG cannot have snapshots, user has to remove the snapshots first in order to add the CG to an
// application
URIQueryResultList cgSnapshotsResults = new URIQueryResultList();
_dbClient.queryByConstraint(getBlockSnapshotByConsistencyGroup(cgUri), cgSnapshotsResults);
Iterator<URI> cgSnapshotsIter = cgSnapshotsResults.iterator();
while (cgSnapshotsIter.hasNext()) {
BlockSnapshot cgSnapshot = _dbClient.queryObject(BlockSnapshot.class, cgSnapshotsIter.next());
if ((cgSnapshot != null) && (!cgSnapshot.getInactive())) {
throw APIException.badRequests.notAllowedWhenVNXCGHasSnapshot();
}
}
nonAppReadyCGVolUris.addAll(cgVolumeURIs);
} else { // non VNX CG volume, or volume in VNX CG with no array replication group
appReadyCGUris.add(cgUri);
appReadyCGVols.addAll(cgVolumes);
}
}
if (!appReadyCGVols.isEmpty()) {
for (Volume cgVol : appReadyCGVols) {
StringSet applications = cgVol.getVolumeGroupIds();
applications.add(application.getId().toString());
cgVol.setVolumeGroupIds(applications);
// handle clones
StringSet fullCopies = cgVol.getFullCopies();
List<Volume> fullCopiesToUpdate = new ArrayList<Volume>();
if (fullCopies != null && !fullCopies.isEmpty()) {
for (String fullCopyId : fullCopies) {
Volume fullCopy = _dbClient.queryObject(Volume.class, URI.create(fullCopyId));
if (fullCopy != null && !fullCopy.getInactive()) {
fullCopy.setFullCopySetName(fullCopy.getReplicationGroupInstance());
fullCopiesToUpdate.add(fullCopy);
}
}
}
if (!fullCopiesToUpdate.isEmpty()) {
_dbClient.updateObject(fullCopiesToUpdate);
}
Operation op = cgVol.getOpStatus().get(taskId);
op.ready();
cgVol.getOpStatus().updateTaskStatus(taskId, op);
}
_dbClient.updateObject(appReadyCGVols);
}
if (!appReadyCGUris.isEmpty()) {
for (URI cgUri : appReadyCGUris) {
BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, cgUri);
if (cg != null && !cg.getInactive()) {
cg.setArrayConsistency(false);
Operation op = cg.getOpStatus().get(taskId);
op.ready();
cg.getOpStatus().updateTaskStatus(taskId, op);
_dbClient.updateObject(cg);
}
}
}
addVolumeList.getVolumes().addAll(nonAppReadyCGVolUris);
_log.info("Added volumes in CG to the application");
return addVolumeList;
}
/**
* Remove volumes from application
*
* @param removeVolumes
* Volumes to be removed
* @param taskId
* @param application
* The application that the volumes are removed from
*/
private void removeVolumesFromApplication(List<Volume> removeVolumes, VolumeGroup application, String taskId) {
Map<URI, List<URI>> cgVolsMap = new HashMap<URI, List<URI>>();
for (Volume volume : removeVolumes) {
URI cgUri = volume.getConsistencyGroup();
if (!NullColumnValueGetter.isNullURI(cgUri)) {
List<URI> vols = cgVolsMap.get(cgUri);
if (vols == null) {
vols = new ArrayList<URI>();
}
vols.add(volume.getId());
cgVolsMap.put(cgUri, vols);
} else {
// Shouldn't happen. The volume is not in CG
throw APIException.badRequests.volumeGroupCantBeUpdated(application.getLabel(),
String.format("The volume %s is not in a consistency group", volume.getLabel()));
}
}
for (Map.Entry<URI, List<URI>> entry : cgVolsMap.entrySet()) {
URI cgUri = entry.getKey();
List<URI> cgVolsToRemove = entry.getValue();
BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, cgUri);
List<Volume> cgVolumes = getActiveCGVolumes(cg);
Set<URI> cgVolumeURIs = new HashSet<URI>();
for (Volume cgVol : cgVolumes) {
cgVolumeURIs.add(cgVol.getId());
}
// Check if all CG volumes are removing from the application
Volume firstVolume = _dbClient.queryObject(Volume.class, cgVolsToRemove.get(0));
if (!cgVolumeURIs.containsAll(cgVolsToRemove) || cgVolsToRemove.size() != cgVolumeURIs.size()) {
throw APIException.badRequests.volumeCantBeRemovedFromVolumeGroup(firstVolume.getLabel(),
"not all volumes in consistency group are in the remove volume list");
}
for (Volume cgVol : cgVolumes) {
StringSet applications = cgVol.getVolumeGroupIds();
if (applications != null && !applications.isEmpty()) {
applications.remove(application.getId().toString());
cgVol.setVolumeGroupIds(applications);
}
// handle clones
StringSet fullCopies = cgVol.getFullCopies();
List<Volume> fullCopiesToUpdate = new ArrayList<Volume>();
if (fullCopies != null && !fullCopies.isEmpty()) {
for (String fullCopyId : fullCopies) {
Volume fullCopy = _dbClient.queryObject(Volume.class, URI.create(fullCopyId));
if (fullCopy != null && !fullCopy.getInactive()) {
fullCopy.setFullCopySetName(NullColumnValueGetter.getNullStr());
fullCopiesToUpdate.add(fullCopy);
}
}
}
if (!fullCopiesToUpdate.isEmpty()) {
_dbClient.updateObject(fullCopiesToUpdate);
}
Operation op = cgVol.getOpStatus().get(taskId);
op.ready();
cgVol.getOpStatus().updateTaskStatus(taskId, op);
}
_dbClient.updateObject(cgVolumes);
// update task status for CGs
Operation op = cg.getOpStatus().get(taskId);
op.ready();
cg.getOpStatus().updateTaskStatus(taskId, op);
_dbClient.updateObject(cg);
}
_log.info("Removed volumes in CG from the application");
}
/**
* Creates tasks against consistency group associated with a request and adds them to the given task list.
*
* @param group
* @param taskList
* @param taskId
* @param operationTypeEnum
*/
protected void addConsistencyGroupTask(URI groupUri, TaskList taskList,
String taskId,
ResourceOperationTypeEnum operationTypeEnum) {
BlockConsistencyGroup group = _dbClient.queryObject(BlockConsistencyGroup.class, groupUri);
Operation op = _dbClient.createTaskOpStatus(BlockConsistencyGroup.class, group.getId(), taskId,
operationTypeEnum);
taskList.getTaskList().add(TaskMapper.toTask(group, taskId, op));
}
/**
* Creates tasks against consistency groups associated with a request and adds them to the given task list.
*
* @param group
* @param taskList
* @param taskId
* @param operationTypeEnum
*/
protected void addVolumeTask(Volume volume, TaskList taskList,
String taskId,
ResourceOperationTypeEnum operationTypeEnum) {
Operation op = _dbClient.createTaskOpStatus(Volume.class, volume.getId(), taskId,
operationTypeEnum);
taskList.getTaskList().add(TaskMapper.toTask(volume, taskId, op));
}
/*
* (non-Javadoc)
*
* @see
* com.emc.storageos.api.service.impl.resource.BlockServiceApi#getReplicationGroupNames(com.emc.storageos.db.client.
* model.VolumeGroup)
*/
@Override
public Collection<? extends String> getReplicationGroupNames(VolumeGroup group) {
List<String> groupNames = new ArrayList<String>();
final List<Volume> volumes = CustomQueryUtility
.queryActiveResourcesByConstraint(_dbClient, Volume.class,
AlternateIdConstraint.Factory.getVolumesByVolumeGroupId(group.getId().toString()));
for (Volume volume : volumes) {
if (NullColumnValueGetter.isNotNullValue(volume.getReplicationGroupInstance())) {
groupNames.add(volume.getReplicationGroupInstance());
}
}
return groupNames;
}
}