/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource.fullcopy;
import static com.emc.storageos.db.client.constraint.ContainmentConstraint.Factory.getVolumesByConsistencyGroup;
import static com.emc.storageos.db.client.util.NullColumnValueGetter.isNullURI;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.Controller;
import com.emc.storageos.api.mapper.BlockMapper;
import com.emc.storageos.api.mapper.TaskMapper;
import com.emc.storageos.api.service.impl.placement.Scheduler;
import com.emc.storageos.api.service.impl.resource.BlockServiceApi;
import com.emc.storageos.api.service.impl.resource.utils.BlockServiceUtils;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
import com.emc.storageos.coordinator.exceptions.RetryableCoordinatorException;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.constraint.AlternateIdConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.BlockConsistencyGroup;
import com.emc.storageos.db.client.model.BlockObject;
import com.emc.storageos.db.client.model.BlockSnapshot;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.client.model.Operation;
import com.emc.storageos.db.client.model.StoragePool;
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.Volume.ReplicationState;
import com.emc.storageos.db.client.model.VolumeGroup;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.emc.storageos.model.ResourceOperationTypeEnum;
import com.emc.storageos.model.TaskList;
import com.emc.storageos.model.TaskResourceRep;
import com.emc.storageos.model.block.VolumeRestRep;
import com.emc.storageos.plugins.common.Constants;
import com.emc.storageos.services.util.StorageDriverManager;
import com.emc.storageos.svcs.errorhandling.model.ServiceCoded;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.volumecontroller.BlockController;
import com.emc.storageos.volumecontroller.ControllerException;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils;
import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper;
/**
* Abstract full copy implementation provides a container for code and utilities
* common to all platform specific implementations.
*/
public abstract class AbstractBlockFullCopyApiImpl implements BlockFullCopyApi {
// A reference to a database client.
protected DbClient _dbClient;
// A reference to the coordinator.
protected CoordinatorClient _coordinator = null;
// A reference to a scheduler.
protected Scheduler _scheduler = null;
// A reference to the full copy manager.
protected BlockFullCopyManager _fullCopyMgr;
// A reference to a logger.
private static final Logger s_logger = LoggerFactory.getLogger(AbstractBlockFullCopyApiImpl.class);
private static StorageDriverManager storageDriverManager = (StorageDriverManager)StorageDriverManager.
getApplicationContext().getBean(StorageDriverManager.STORAGE_DRIVER_MANAGER);
/**
* Constructor
*
* @param dbClient A reference to a database client.
* @param coordinator A reference to the coordinator.
* @param scheduler A reference to the scheduler.
* @param fullCopyMgr A reference to the full copy manager.
*/
public AbstractBlockFullCopyApiImpl(DbClient dbClient, CoordinatorClient coordinator,
Scheduler scheduler, BlockFullCopyManager fullCopyMgr) {
_dbClient = dbClient;
_coordinator = coordinator;
_scheduler = scheduler;
_fullCopyMgr = fullCopyMgr;
}
/**
* {@inheritDoc}
*/
@Override
public List<BlockObject> getAllSourceObjectsForFullCopyRequest(BlockObject fcSourceObj) {
List<BlockObject> fcSourceObjList = new ArrayList<BlockObject>();
if (URIUtil.isType(fcSourceObj.getId(), BlockSnapshot.class)) {
// For snapshots we only make a fully copy for the
// passed snapshot.
fcSourceObjList.add(fcSourceObj);
} else {
// Otherwise, if the volume is in a CG, then we create
// a full copy for each volume in the CG.
Volume fcSourceVolume = (Volume) fcSourceObj;
URI cgURI = fcSourceVolume.getConsistencyGroup();
if (!isNullURI(cgURI)) {
// if volume is part of COPY type Volume Group, get only the Array Group volumes
if (fcSourceVolume.getApplication(_dbClient) != null) {
fcSourceObjList.addAll(
ControllerUtils.getVolumesPartOfRG(fcSourceVolume.getStorageController(),
fcSourceVolume.getReplicationGroupInstance(), _dbClient));
} else {
BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, cgURI);
fcSourceObjList.addAll(getActiveCGVolumes(cg));
}
} else {
fcSourceObjList.add(fcSourceObj);
}
}
return fcSourceObjList;
}
/**
* {@inheritDoc}
*/
@Override
public Map<URI, Volume> getFullCopySetMap(BlockObject fcSourceObj,
Volume fullCopyVolume) {
Map<URI, Volume> fullCopyMap = new HashMap<URI, Volume>();
URI cgURI = fcSourceObj.getConsistencyGroup();
if ((!isNullURI(cgURI))
&& (!BlockFullCopyUtils.isFullCopyDetached(fullCopyVolume, _dbClient))
&& (NullColumnValueGetter.isNotNullValue(fullCopyVolume.getReplicationGroupInstance()))) {
// If the full copy is not detached and the source is
// in a CG, then the full copy is treated as a set and
// there should be a full copy for each source in the
// CG and they should be part of the same replication
// group instance.
URIQueryResultList queryResults = new URIQueryResultList();
_dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getVolumeReplicationGroupInstanceConstraint(fullCopyVolume
.getReplicationGroupInstance()), queryResults);
Iterator<URI> resultsIter = queryResults.iterator();
while (resultsIter.hasNext()) {
URI fullCopyURI = resultsIter.next();
fullCopyMap.put(fullCopyURI,
_dbClient.queryObject(Volume.class, fullCopyURI));
}
} else {
fullCopyMap.put(fullCopyVolume.getId(), fullCopyVolume);
}
return fullCopyMap;
}
/**
* Gets the active volumes in the passed consistency group.
*
* @param cg A reference to a consistency group.
*
* @return The active volumes in the passed consistency group.
*/
protected List<Volume> getActiveCGVolumes(BlockConsistencyGroup cg) {
List<Volume> volumeList = new ArrayList<Volume>();
URIQueryResultList uriQueryResultList = new URIQueryResultList();
_dbClient.queryByConstraint(getVolumesByConsistencyGroup(cg.getId()),
uriQueryResultList);
Iterator<Volume> volumeIterator = _dbClient.queryIterativeObjects(Volume.class,
uriQueryResultList, true);
while (volumeIterator.hasNext()) {
Volume volume = volumeIterator.next();
volumeList.add(volume);
}
return volumeList;
}
/**
* {@inheritDoc}
*/
@Override
public void validateFullCopyCreateRequest(List<BlockObject> fcSourceObjList, int count) {
// Verify CG volume requested count and CG snapshot
if (!fcSourceObjList.isEmpty()) {
BlockObject fcSourceObj = fcSourceObjList.get(0);
URI cgURI = fcSourceObj.getConsistencyGroup();
if (!isNullURI(cgURI)) {
URI fcSourceURI = fcSourceObj.getId();
if (URIUtil.isType(fcSourceURI, Volume.class)) {
verifyCGVolumeRequestCount(count);
} else {
verifyCGSnapshotRequest();
}
}
}
// Verify full copy is supported for each full copy source object's storage pool.
for (BlockObject fcSourceObj : fcSourceObjList) {
// Verify full copy is supported for each full copy
// source object's storage pool. The pool could be
// null when called by the VPLEX implementation.
StoragePool storagePool = BlockFullCopyUtils.queryFullCopySourceStoragePool(fcSourceObj, _dbClient);
if (storagePool != null) {
verifyFullCopySupportedForStoragePool(storagePool);
}
// Verify the requested copy count.
verifyFullCopyRequestCount(fcSourceObj, count);
}
}
/**
* {@inheritDoc}
*/
@Override
public void validateSnapshotCreateRequest(Volume requestedVolume, List<Volume> volumesToSnap) {
// Nothing to do by default.
}
/**
* Verify if full copy is supported for the storage pool with the passed
* URI.
*
* @param storagePool A reference to the storage pool for the full copy
* source.
*/
protected void verifyFullCopySupportedForStoragePool(StoragePool storagePool) {
URI storageSystemUri = storagePool.getStorageDevice();
StorageSystem storageSystem = (StorageSystem) _dbClient.queryObject(storageSystemUri);
if (!storageDriverManager.isDriverManaged(storageSystem.getSystemType())) {
// for driver managed systems we allow full copy.
StringSet copyTypes = storagePool.getSupportedCopyTypes();
if ((copyTypes == null) || (!copyTypes.contains(StoragePool.CopyTypes.UNSYNC_UNASSOC.name()))) {
throw APIException.badRequests.fullCopyNotSupportedOnArray(storagePool.getStorageDevice());
}
}
}
/**
* {@inheritDoc}
*/
@Override
public TaskList create(List<BlockObject> fcSourceObjList, VirtualArray varray,
String name, boolean createInactive, int count, String taskId) {
throw APIException.methodNotAllowed.notSupported();
}
/**
* {@inheritDoc}
*/
@Override
public TaskList activate(BlockObject fcSourceObj, Volume fullCopyVolume) {
// Create the task list.
TaskList taskList = new TaskList();
// Create a unique task id.
String taskId = UUID.randomUUID().toString();
// If the source is in a CG, then we will activate the corresponding
// full copies for all the volumes in the CG. Since we did not allow
// full copies for volumes or snaps in CGs prior to Jedi, there should
// be a full copy for all volumes in the CG.
Map<URI, Volume> fullCopyMap = getFullCopySetMap(fcSourceObj, fullCopyVolume);
Set<URI> fullCopyURIs = fullCopyMap.keySet();
// The full copy manager will not call activate if the full copy is
// detached, so if the state is not inactive, then it must have
// already been activated. In this case return activate action is
// completed successfully. Otherwise, send activate full copy request
// to controller.
if (!BlockFullCopyUtils.isFullCopyInactive(fullCopyVolume, _dbClient)) {
Operation op = new Operation();
op.setResourceType(ResourceOperationTypeEnum.ACTIVATE_VOLUME_FULL_COPY);
op.ready("Full copy is already activated");
for (URI fullCopyURI : fullCopyURIs) {
_dbClient.createTaskOpStatus(Volume.class, fullCopyURI, taskId, op);
TaskResourceRep task = TaskMapper.toTask(fullCopyMap.get(fullCopyURI),
taskId, op);
taskList.addTask(task);
}
} else {
StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class,
fullCopyVolume.getStorageController());
BlockController controller = getController(BlockController.class,
storageSystem.getSystemType());
for (URI fullCopyURI : fullCopyURIs) {
Operation op = _dbClient.createTaskOpStatus(Volume.class, fullCopyURI,
taskId, ResourceOperationTypeEnum.ACTIVATE_VOLUME_FULL_COPY);
fullCopyMap.get(fullCopyURI).getOpStatus().put(taskId, op);
TaskResourceRep fullCopyVolumeTask = TaskMapper.toTask(
fullCopyMap.get(fullCopyURI), taskId, op);
taskList.getTaskList().add(fullCopyVolumeTask);
}
addConsistencyGroupTasks(Arrays.asList(fcSourceObj), taskList, taskId,
ResourceOperationTypeEnum.ACTIVATE_CONSISTENCY_GROUP_FULL_COPY);
try {
controller.activateFullCopy(storageSystem.getId(), new ArrayList<URI>(
fullCopyURIs), taskId);
} catch (ControllerException ce) {
s_logger.error("Failed to activate volume full copy {}", fullCopyVolume.getId(), ce);
handleFailedRequest(taskId, taskList,
new ArrayList<Volume>(fullCopyMap.values()), ce, false);
}
}
return taskList;
}
/**
* {@inheritDoc}
*/
@Override
public TaskList detach(BlockObject fcSourceObj, Volume fullCopyVolume) {
// Create the task list.
TaskList taskList = new TaskList();
// Create a unique task id.
String taskId = UUID.randomUUID().toString();
// If the source is in a CG, then we will activate the corresponding
// full copies for all the volumes in the CG. Since we did not allow
// full copies for volumes or snaps in CGs prior to Jedi, there should
// be a full copy for all volumes in the CG.
Map<URI, Volume> fullCopyMap = getFullCopySetMap(fcSourceObj, fullCopyVolume);
Set<URI> fullCopyURIs = fullCopyMap.keySet();
// If full copy volume is already detached, return detach action is
// completed successfully. Also, if the state is inactive, then it was
// never activated and therefore is also really detached. Otherwise,
// send detach full copy request to controller.
if ((BlockFullCopyUtils.isFullCopyDetached(fullCopyVolume, _dbClient)) ||
(BlockFullCopyUtils.isFullCopyInactive(fullCopyVolume, _dbClient))) {
Operation op = new Operation();
op.setResourceType(ResourceOperationTypeEnum.DETACH_VOLUME_FULL_COPY);
op.ready("Full copy is already detached");
for (URI fullCopyURI : fullCopyURIs) {
_dbClient.createTaskOpStatus(Volume.class, fullCopyURI, taskId, op);
TaskResourceRep task = TaskMapper.toTask(fullCopyMap.get(fullCopyURI),
taskId, op);
taskList.addTask(task);
if (!BlockFullCopyUtils.isFullCopyDetached(fullCopyVolume, _dbClient)) {
// Make sure the replica state is set to detached.
Volume volume = fullCopyVolume;
if (!fullCopyURI.equals(fullCopyVolume.getId())) {
volume = _dbClient.queryObject(Volume.class, fullCopyURI);
}
ReplicationUtils.removeDetachedFullCopyFromSourceFullCopiesList(volume, _dbClient);
volume.setAssociatedSourceVolume(NullColumnValueGetter.getNullURI());
volume.setReplicaState(ReplicationState.DETACHED.name());
_dbClient.persistObject(volume);
}
}
} else {
StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class,
fullCopyVolume.getStorageController());
BlockController controller = getController(BlockController.class,
storageSystem.getSystemType());
for (URI fullCopyURI : fullCopyURIs) {
Operation op = _dbClient.createTaskOpStatus(Volume.class, fullCopyURI,
taskId, ResourceOperationTypeEnum.DETACH_VOLUME_FULL_COPY);
fullCopyMap.get(fullCopyURI).getOpStatus().put(taskId, op);
TaskResourceRep fullCopyVolumeTask = TaskMapper.toTask(
fullCopyMap.get(fullCopyURI), taskId, op);
taskList.getTaskList().add(fullCopyVolumeTask);
}
if (NullColumnValueGetter.isNotNullValue(fullCopyVolume.getReplicationGroupInstance())) {
addConsistencyGroupTasks(Arrays.asList(fcSourceObj), taskList, taskId,
ResourceOperationTypeEnum.DETACH_CONSISTENCY_GROUP_FULL_COPY);
}
try {
controller.detachFullCopy(storageSystem.getId(), new ArrayList<URI>(
fullCopyURIs), taskId);
} catch (ControllerException ce) {
s_logger.error("Failed to detach volume full copy {}", fullCopyVolume.getId(), ce);
handleFailedRequest(taskId, taskList,
new ArrayList<Volume>(fullCopyMap.values()), ce, false);
}
}
return taskList;
}
/**
* {@inheritDoc}
*/
@Override
public TaskList restoreSource(Volume sourceVolume, Volume fullCopyVolume) {
throw APIException.methodNotAllowed.notSupported();
}
/**
* {@inheritDoc}
*/
@Override
public TaskList resynchronizeCopy(Volume sourceVolume, Volume fullCopyVolume) {
throw APIException.methodNotAllowed.notSupported();
}
/**
* {@inheritDoc}
*/
@Override
public TaskList establishVolumeAndFullCopyGroupRelation(Volume sourceVolume, Volume fullCopyVolume) {
throw APIException.methodNotAllowed.notSupported();
}
/**
* {@inheritDoc}
*/
@Override
public VolumeRestRep checkProgress(URI sourceURI, Volume fullCopyVolume) {
Integer result = getSyncPercentage(sourceURI, fullCopyVolume);
VolumeRestRep volumeRestRep = BlockMapper.map(_dbClient, fullCopyVolume);
volumeRestRep.getProtection().getFullCopyRep().setPercentSynced(result);
return volumeRestRep;
}
/**
* {@inheritDoc}
*/
@Override
public boolean volumeCanBeDeleted(Volume volume) {
/**
* Delete volume api call will delete all its related replicas for VMAX using SMI 8.0.3.
* Hence vmax using 8.0.3 can be delete even if volume has replicas.
*/
if (volume.isInCG() && BlockServiceUtils.checkCGVolumeCanBeAddedOrRemoved(null, volume, _dbClient)) {
return true;
}
boolean volumeCanBeDeleted = true;
// Verify that a volume that is a full copy is detached.
if ((BlockFullCopyUtils.isVolumeFullCopy(volume, _dbClient)) &&
(!BlockFullCopyUtils.isFullCopyDetached(volume, _dbClient))) {
volumeCanBeDeleted = false;
}
// Verify that a volume that is a full copy source is detached
// from those full copies.
if ((volumeCanBeDeleted) && (BlockFullCopyUtils.isVolumeFullCopySource(volume, _dbClient))) {
volumeCanBeDeleted = BlockFullCopyUtils.volumeDetachedFromFullCopies(volume, _dbClient);
}
return volumeCanBeDeleted;
}
/**
* {@inheritDoc}
*/
@Override
public boolean volumeCanBeExpanded(Volume volume) {
if (((BlockFullCopyUtils.isVolumeFullCopy(volume, _dbClient)) &&
(!BlockFullCopyUtils.isFullCopyDetached(volume, _dbClient))) ||
((BlockFullCopyUtils.isVolumeFullCopySource(volume, _dbClient)) &&
(!BlockFullCopyUtils.volumeDetachedFromFullCopies(volume, _dbClient)))) {
return false;
}
return true;
}
/**
* Gets the varray from cache.
*
* @param vArrayCache the varray cache
* @param vArrayURI the virtual array
* @return the varray from cache
*/
protected VirtualArray getVarrayFromCache(Map<URI, VirtualArray> vArrayCache, URI vArrayURI) {
if (vArrayCache.get(vArrayURI) == null) {
VirtualArray vArray = _dbClient.queryObject(VirtualArray.class, vArrayURI);
vArrayCache.put(vArrayURI, vArray);
}
return vArrayCache.get(vArrayURI);
}
/**
* Gets the percent synchronized for the passed full copy volume.
*
* @param sourceURI The URI of the full copy source.
* @param fullCopyVolume A reference to the full copy volume.
*
* @return The percent synchronized.
*/
protected Integer getSyncPercentage(URI sourceURI, Volume fullCopyVolume) {
Integer result = null;
URI fullCopyURI = fullCopyVolume.getId();
if (!BlockFullCopyUtils.isFullCopyInactive(fullCopyVolume, _dbClient)) {
String taskId = UUID.randomUUID().toString();
StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class,
fullCopyVolume.getStorageController());
BlockController controller = getController(BlockController.class,
storageSystem.getSystemType());
try {
result = controller.checkSyncProgress(storageSystem.getId(),
sourceURI, fullCopyURI, taskId);
} catch (ControllerException ce) {
s_logger.error("Failed to check synchronization progress for volume full copy {}",
fullCopyURI, ce);
}
} else {
result = 0;
}
if (result == null) {
throw APIException.badRequests.protectionUnableToGetSynchronizationProgress();
}
return result;
}
/**
* Looks up controller dependency for given hardware type.
* If cannot locate controller for defined hardware type, lookup controller for
* EXTERNALDEVICE.
*
* @param clazz controller interface
* @param hw hardware name
* @param <T>
* @return
*/
protected <T extends Controller> T getController(Class<T> clazz, String hw) {
T controller;
try {
controller = _coordinator.locateService(
clazz, BlockServiceApi.CONTROLLER_SVC,
BlockServiceApi.CONTROLLER_SVC_VER, hw, clazz.getSimpleName());
} catch (RetryableCoordinatorException rex) {
controller = _coordinator.locateService(
clazz, BlockServiceApi.CONTROLLER_SVC,
BlockServiceApi.CONTROLLER_SVC_VER, Constants.EXTERNALDEVICE, clazz.getSimpleName());
}
return controller;
}
/**
* Creates a capabilities wrapper specifying some parameters for the
* full copy create request.
*
* @param fcSourceObj A reference to the full copy source.
* @param vpool A reference to the source's virtual pool.
* @param count A count of the number of full copies requested.
*
* @return VirtualPoolCapabilityValuesWrapper
*/
protected VirtualPoolCapabilityValuesWrapper getCapabilitiesForFullCopyCreate(
BlockObject fcSourceObj, VirtualPool vpool, int count) {
VirtualPoolCapabilityValuesWrapper capabilities = new VirtualPoolCapabilityValuesWrapper();
capabilities.put(VirtualPoolCapabilityValuesWrapper.RESOURCE_COUNT, count);
capabilities.put(VirtualPoolCapabilityValuesWrapper.SIZE,
BlockFullCopyUtils.getCapacityForFullCopySource(fcSourceObj, _dbClient));
if (VirtualPool.ProvisioningType.Thin.toString().equalsIgnoreCase(
vpool.getSupportedProvisioningType())) {
capabilities.put(VirtualPoolCapabilityValuesWrapper.THIN_PROVISIONING, Boolean.TRUE);
// To guarantee that storage pool for a copy has enough physical
// space to contain current allocated capacity of thin source volume
capabilities.put(VirtualPoolCapabilityValuesWrapper.THIN_VOLUME_PRE_ALLOCATE_SIZE,
BlockFullCopyUtils.getAllocatedCapacityForFullCopySource(fcSourceObj, _dbClient));
}
return capabilities;
}
/**
* Sorts the passed list of full copy source objects based
* on the natural sort order of their labels. Used to align
* the labels for the full copies with their sources. For example
* say you have a CG with two volumes foo-1 and foo-2, and you
* then create a full copy of one of these volumes. Because
* they are in a CG, a full copy is created for each volume
* in the CG. When we create the full copies with a name of
* bar, then we want the full copy for foo-1 to be bar-1, and
* the full copy for foo-2 to be bar-2. However, when you get
* the volumes in the CG, there is no guarantee of order. Now
* there is no restriction that volumes in a CG are named like
* this, but often they are because often one would create
* multiple volumes in a CG in a single request. So, in this
* simple case, then the full copy names should align. We can
* always create a more sophisticated comparator for the sort
* routine if something more elaborate is desired.
*
* @param fcSourceObjects A list of full copy source objects.
*
* @return The objects sorted in natural order by their labels.
*/
protected List<BlockObject> sortFullCopySourceList(List<BlockObject> fcSourceObjects) {
List<BlockObject> sortedSourceObjects = new ArrayList<BlockObject>();
// Put the full copy source objects in a map
// keyed by label.
Map<String, BlockObject> fcSourcObjectsMap = new HashMap<String, BlockObject>();
for (BlockObject fcSourceObject : fcSourceObjects) {
fcSourcObjectsMap.put(fcSourceObject.getLabel(), fcSourceObject);
}
// Create a list of the labels and sort them in natural order.
List<String> fcSourceLabels = new ArrayList<String>(fcSourcObjectsMap.keySet());
Collections.sort(fcSourceLabels);
// Iterate over the sorted labels adding them to the list.
for (String fcSourceLabel : fcSourceLabels) {
sortedSourceObjects.add(fcSourcObjectsMap.get(fcSourceLabel));
}
return sortedSourceObjects;
}
/**
* Verify the requested full copy count is valid.
*
* @param fcSourceObj A reference to the full copy source.
* @param count The requested full copy count.
*/
protected void verifyFullCopyRequestCount(BlockObject fcSourceObj, int count) {
BlockFullCopyUtils.validateActiveFullCopyCount(fcSourceObj, count, _dbClient);
}
/**
* Verify the requested full copy count for volume in CG is valid.
* For array where group clone is not supported, override this method in platform specific impl.
*
* @param count The requested full copy count.
*/
protected void verifyCGVolumeRequestCount(int count) {
// We only allow you to create a single full copy at a time
// for volumes in a consistency group if group clone is supported.
if (count > 1) {
throw APIException.badRequests.invalidFullCopyCountForVolumesInConsistencyGroup();
}
}
/**
* Verify the requested full copy for snapshot of volume in CG.
* For array where group clone is not supported, override this method in platform specific impl.
*/
protected void verifyCGSnapshotRequest() {
// We don't support creating full copies of snapshots in consistency groups.
throw APIException.badRequests.fullCopyNotSupportedForConsistencyGroup();
}
/**
* Updates passed volume and tasks on failure.
*
* @param taskId The unique task id.
* @param taskList A list of the task responses.
* @param volumes The list of volumes for the operation.
* @param sc A reference to the error.
* @param markInactive true to mark the volumes inactive, false otherwise.
*/
protected void handleFailedRequest(String taskId, TaskList taskList,
List<Volume> volumes, ServiceCoded sc, boolean markInactive) {
for (TaskResourceRep volumeTask : taskList.getTaskList()) {
volumeTask.setState(Operation.Status.error.name());
volumeTask.setMessage(sc.getMessage());
URI resourceURI = volumeTask.getResource().getId();
DataObject dataObject = null;
if (URIUtil.isType(resourceURI, BlockConsistencyGroup.class)) {
dataObject = _dbClient.queryObject(BlockConsistencyGroup.class, resourceURI);
} else if (URIUtil.isType(resourceURI, VolumeGroup.class)) {
dataObject = _dbClient.queryObject(VolumeGroup.class, resourceURI);
} else {
dataObject = _dbClient.queryObject(Volume.class, resourceURI);
}
Operation op = dataObject.getOpStatus().get(taskId);
if (op != null) {
op.error(sc);
dataObject.getOpStatus().updateTaskStatus(taskId, op);
_dbClient.updateObject(dataObject);
}
}
if (markInactive) {
for (Volume volume : volumes) {
volume.setInactive(true);
_dbClient.updateObject(volume);
}
}
}
/**
* Creates tasks against consistency groups associated with a request and adds them to the given task list.
*
* @param objects
* @param taskList
* @param taskId
* @param operationTypeEnum
* @param <T>
*/
protected <T extends BlockObject> void addConsistencyGroupTasks(List<T> objects, TaskList taskList, String taskId,
ResourceOperationTypeEnum operationTypeEnum) {
Set<URI> consistencyGroups = new HashSet<>();
for (T object : objects) {
if (!isNullURI(object.getConsistencyGroup())) {
consistencyGroups.add(object.getConsistencyGroup());
}
}
if (consistencyGroups.isEmpty()) {
return;
}
List<BlockConsistencyGroup> groups = _dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroups);
for (BlockConsistencyGroup group : groups) {
Operation op = _dbClient.createTaskOpStatus(BlockConsistencyGroup.class, group.getId(), taskId,
operationTypeEnum);
taskList.getTaskList().add(TaskMapper.toTask(group, taskId, op));
}
}
/**
* returns the list of tasks required for create the full copy volumes.
*
* @param fcSourceObj
* A reference to a full copy source.
* @param fullCopyVolumes
* A list of the prepared full copy volumes.
* @param taskId
* The unique task identifier
*
* @return TaskList
*/
protected TaskList getTasksForCreateFullCopy(BlockObject fcSourceObj, List<Volume> fullCopyVolumes, String taskId) {
if (fcSourceObj == null) {
throw APIException.badRequests.fullCopyInternalError("create");
}
TaskList taskList = new TaskList();
for (Volume volume : fullCopyVolumes) {
Operation op = _dbClient.createTaskOpStatus(Volume.class, volume.getId(), taskId,
ResourceOperationTypeEnum.CREATE_VOLUME_FULL_COPY);
volume.getOpStatus().put(taskId, op);
TaskResourceRep volumeTask = TaskMapper.toTask(volume, taskId, op);
taskList.getTaskList().add(volumeTask);
}
// if Volume is part of Application (COPY type VolumeGroup)
VolumeGroup volumeGroup = (fcSourceObj instanceof Volume) ? ((Volume) fcSourceObj).getApplication(_dbClient) : null;
if (volumeGroup != null && !ControllerUtils.checkVolumeForVolumeGroupPartialRequest(_dbClient, (Volume) fcSourceObj)) {
Operation op = _dbClient.createTaskOpStatus(VolumeGroup.class, volumeGroup.getId(), taskId,
ResourceOperationTypeEnum.CREATE_VOLUME_GROUP_FULL_COPY);
taskList.getTaskList().add(TaskMapper.toTask(volumeGroup, taskId, op));
// get all volumes to create tasks for all CGs involved
List<Volume> volumes = ControllerUtils.getVolumeGroupVolumes(_dbClient, volumeGroup);
addConsistencyGroupTasks(volumes, taskList, taskId, ResourceOperationTypeEnum.CREATE_CONSISTENCY_GROUP_FULL_COPY);
} else {
addConsistencyGroupTasks(Arrays.asList(fcSourceObj), taskList, taskId,
ResourceOperationTypeEnum.CREATE_CONSISTENCY_GROUP_FULL_COPY);
}
return taskList;
}
/**
* Set volumes to inactive state.
* Release reserved capacity for volumes in their storage pools.
* @param volumesList volumes to process.
*/
public void handlePlacementFailure(List<Volume> volumesList) {
Map<URI, StoragePool> uriToStoragePool = new HashMap<>();
Map<URI, List<String>> storagePoolUriToVolumes = new HashMap<>();
for (Volume volume : volumesList) {
volume.setInactive(true);
URI storagePoolUri = volume.getPool();
if (!(URIUtil.isNull(storagePoolUri) || URIUtil.isNull(volume.getId()))) {
List<String> poolVolumes = storagePoolUriToVolumes.get(storagePoolUri);
if (poolVolumes == null) {
poolVolumes = new ArrayList<>();
storagePoolUriToVolumes.put(storagePoolUri, poolVolumes);
StoragePool storagePool = _dbClient.queryObject(StoragePool.class, storagePoolUri);
if (storagePool != null) {
uriToStoragePool.put(storagePoolUri, storagePool);
}
}
poolVolumes.add(volume.getId().toString());
}
}
_dbClient.updateObject(volumesList);
for (URI poolUri : uriToStoragePool.keySet()) {
StoragePool pool = uriToStoragePool.get(poolUri);
List<String> volumes = storagePoolUriToVolumes.get(poolUri);
pool.removeReservedCapacityForVolumes(volumes);
_dbClient.updateObject(pool);
}
}
}