/*
* Copyright (c) 2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource.snapshot;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.api.service.authorization.PermissionsHelper;
import com.emc.storageos.api.service.impl.resource.fullcopy.BlockFullCopyManager;
import com.emc.storageos.api.service.impl.resource.utils.BlockServiceUtils;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
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.ContainmentConstraint;
import com.emc.storageos.db.client.model.BlockObject;
import com.emc.storageos.db.client.model.BlockSnapshot;
import com.emc.storageos.db.client.model.BlockSnapshotSession;
import com.emc.storageos.db.client.model.DiscoveredDataObject;
import com.emc.storageos.db.client.model.NamedURI;
import com.emc.storageos.db.client.model.Project;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.client.util.CustomQueryUtility;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.util.VPlexUtil;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.vplexcontroller.VPlexController;
/**
* Block snapshot session implementation for volumes on VPLEX systems.
*/
public class VPlexBlockSnapshotSessionApiImpl extends DefaultBlockSnapshotSessionApiImpl {
private static final Logger s_logger = LoggerFactory.getLogger(VPlexBlockSnapshotSessionApiImpl.class);
/**
* Private default constructor should not be called outside class.
*/
@SuppressWarnings("unused")
private VPlexBlockSnapshotSessionApiImpl() {
super();
}
/**
* Constructor.
*
* @param dbClient A reference to a data base client.
* @param coordinator A reference to the coordinator client.
* @param permissionsHelper A reference to a permission helper.
* @param securityContext A reference to the security context.
* @param blockSnapshotSessionMgr A reference to the snapshot session manager.
*/
public VPlexBlockSnapshotSessionApiImpl(DbClient dbClient, CoordinatorClient coordinator, PermissionsHelper permissionsHelper,
SecurityContext securityContext, BlockSnapshotSessionManager blockSnapshotSessionMgr) {
super(dbClient, coordinator, permissionsHelper, securityContext, blockSnapshotSessionMgr);
}
/**
* {@inheritDoc}
*/
@Override
public void validateSnapshotSessionCreateRequest(BlockObject requestedSourceObj, List<BlockObject> sourceObjList, Project project,
String name, int newTargetsCount, String newTargetsName, String newTargetCopyMode, boolean skipInternalCheck,
BlockFullCopyManager fcManager) {
// We can only create a snapshot session for a VPLEX volume, where the
// source side backend volume supports the creation of a snapshot session.
for (BlockObject sourceObj : sourceObjList) {
URI sourceURI = sourceObj.getId();
if (URIUtil.isType(sourceURI, Volume.class)) {
// Get the platform specific implementation for the source side
// backend storage system and call the validation routine.
Volume vplexVolume = (Volume) sourceObj;
BlockObject srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
snapSessionImpl.validateSnapshotSessionCreateRequest(srcSideBackendVolume, Arrays.asList(srcSideBackendVolume),
project, name, newTargetsCount, newTargetsName, newTargetCopyMode, true, fcManager);
// Check for pending tasks on the VPLEX source volume.
checkForPendingTasks(vplexVolume, vplexVolume.getTenant().getURI());
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void createSnapshotSession(BlockObject sourceObj, URI snapSessionURI,
List<List<URI>> snapSessionSnapshotURIs, String copyMode, String taskId) {
if (URIUtil.isType(sourceObj.getId(), Volume.class)) {
// Get the platform specific implementation for the source side
// backend storage system and call the create method.
Volume vplexVolume = (Volume) sourceObj;
BlockObject srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
snapSessionImpl.createSnapshotSession(srcSideBackendVolume, snapSessionURI, snapSessionSnapshotURIs, copyMode, taskId);
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public void validateLinkNewTargetsRequest(BlockObject snapSessionSourceObj, Project project, int newTargetsCount,
String newTargetsName, String newTargetCopyMode) {
URI sourceURI = snapSessionSourceObj.getId();
if (URIUtil.isType(sourceURI, Volume.class)) {
// Get the platform specific implementation for the source side
// backend storage system and call the validation routine.
Volume vplexVolume = (Volume) snapSessionSourceObj;
BlockObject srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
snapSessionImpl.validateLinkNewTargetsRequest(srcSideBackendVolume, project, newTargetsCount, newTargetsName,
newTargetCopyMode);
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public void linkNewTargetVolumesToSnapshotSession(BlockObject snapSessionSourceObj, BlockSnapshotSession snapSession,
List<List<URI>> snapshotURIs, String copyMode, String taskId) {
if (URIUtil.isType(snapSessionSourceObj.getId(), Volume.class)) {
// Get the platform specific implementation for the source side
// backend storage system and call the link method.
Volume vplexVolume = (Volume) snapSessionSourceObj;
BlockObject srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
snapSessionImpl.linkNewTargetVolumesToSnapshotSession(srcSideBackendVolume, snapSession, snapshotURIs, copyMode, taskId);
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public void validateRelinkSnapshotSessionTargets(BlockObject snapSessionSourceObj, BlockSnapshotSession tgtSnapSession,
Project project, List<URI> snapshotURIs, UriInfo uriInfo) {
URI sourceURI = snapSessionSourceObj.getId();
if (URIUtil.isType(sourceURI, Volume.class)) {
// Get the platform specific implementation for the source side
// backend storage system and call the validation routine.
Volume vplexVolume = (Volume) snapSessionSourceObj;
BlockObject srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
snapSessionImpl.validateRelinkSnapshotSessionTargets(srcSideBackendVolume, tgtSnapSession, project, snapshotURIs, uriInfo);
// If the targets to be re-linked are in replication groups, then all targets
// in the group will be re-linked. So, we need to find all snapshots that will
// be re-linked. First make sure we only have a single snapshot per replication
// group so we don't process any twice.
List<URI> filteredSnapshotURIs = new ArrayList<URI>();
if (tgtSnapSession.hasConsistencyGroup()
&& NullColumnValueGetter.isNotNullValue(tgtSnapSession.getReplicationGroupInstance())) {
filteredSnapshotURIs.addAll(ControllerUtils.ensureOneSnapshotPerReplicationGroup(snapshotURIs, _dbClient));
} else {
filteredSnapshotURIs.addAll(snapshotURIs);
}
List<BlockSnapshot> allSnapshots = new ArrayList<BlockSnapshot>();
for (URI snapshotURI : snapshotURIs) {
BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, snapshotURI);
allSnapshots.addAll(ControllerUtils.getSnapshotsPartOfReplicationGroup(snapshot, _dbClient));
}
// Since it is possible to expose a VPLEX backend snapshot as a VPLEX volume,
// it is possible that a linked target being re-linked has been exposed as a
// VPLEX volume. If that is the case, then what this essentially amounts to
// is that the linked target is being restored with the data from the target
// session. As such, the same restrictions for restoring a snapshot session apply
// here, but not for the snapshot session source objects, and instead for the
// VPLEX volumes built on top of the linked targets.
List<Volume> vplexVolumesBuiltOnSnapshots = VPlexUtil.getVPlexVolumesBuiltOnSnapshots(allSnapshots, _dbClient);
for (Volume vplexVolumesBuiltOnSnapshot : vplexVolumesBuiltOnSnapshots) {
// Check for pending tasks on the VPLEX source volume.
checkForPendingTasks(vplexVolumesBuiltOnSnapshot, vplexVolumesBuiltOnSnapshot.getTenant().getURI());
// Verify no active mirrors on the VPLEX volume.
verifyActiveMirrors(vplexVolumesBuiltOnSnapshot);
}
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public void relinkTargetVolumesToSnapshotSession(BlockObject snapSessionSourceObj, BlockSnapshotSession tgtSnapSession,
List<URI> snapshotURIs, String taskId) {
// Because the source is a VPLEX volume, it is possible that the targets being
// re-linked have VPLEX volumes built on top of them. Because the target is
// being re-linked, that data on the target will change. This means we may have
// to perform operations on the VPLEX volume to ensure it recognizes that the
// data has been changed.
if (URIUtil.isType(snapSessionSourceObj.getId(), Volume.class)) {
// Invoke VLPEX controller.
Volume vplexVolume = (Volume) snapSessionSourceObj;
URI vplexURI = vplexVolume.getStorageController();
VPlexController controller = getController(VPlexController.class,
DiscoveredDataObject.Type.vplex.toString());
controller.relinkTargetsToSnapshotSession(vplexURI, tgtSnapSession.getId(), snapshotURIs, taskId);
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public void validateUnlinkSnapshotSessionTargets(BlockSnapshotSession snapSession, BlockObject snapSessionSourceObj, Project project,
Map<URI, Boolean> targetMap, UriInfo uriInfo) {
URI sourceURI = snapSessionSourceObj.getId();
if (URIUtil.isType(sourceURI, Volume.class)) {
// Get the platform specific implementation for the source side
// backend storage system and call the validation routine.
Volume vplexVolume = (Volume) snapSessionSourceObj;
BlockObject srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
snapSessionImpl.validateUnlinkSnapshotSessionTargets(snapSession, srcSideBackendVolume, project, targetMap,
uriInfo);
// For VPLEX, the linked target volume must be deleted when they are unlinked.
// If we allow this, then you end up with a public Volume instance that is not
// a VPLEX volume, but has a vpool that specifies VPLEX HA. This causes many
// problems, because we end up using the VPlexBlockServiceApiImpl to perform
// block operations on a non-VPLEX volume.
for (Entry<URI, Boolean> targetEntry : targetMap.entrySet()) {
URI snapshotURI = targetEntry.getKey();
Boolean deleteTarget = targetEntry.getValue();
if (Boolean.FALSE == deleteTarget) {
// For VPLEX, the linked target volume must be deleted when they are unlinked.
// If we allow this, then you end up with a public Volume instance that is not
// a VPLEX volume, but has a vpool that specifies VPLEX HA. This causes many
// problems, because we end up using the VPlexBlockServiceApiImpl to perform
// block operations on a non-VPLEX volume.
throw APIException.badRequests.mustDeleteTargetsOnUnlinkForVPlex();
} else {
// Don't allow if there is a VPLEX volume built on the linked target volume.
// The VPLEX volume must be deleted first.
BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, snapshotURI);
String snapshotNativeGuid = snapshot.getNativeGuid();
List<Volume> volumesWithSameNativeGuid = CustomQueryUtility.getActiveVolumeByNativeGuid(_dbClient, snapshotNativeGuid);
if (!volumesWithSameNativeGuid.isEmpty()) {
// There should only be one and it should be a backend volume for
// a VPLEX volume.
List<Volume> vplexVolumes = CustomQueryUtility.queryActiveResourcesByConstraint(
_dbClient, Volume.class, AlternateIdConstraint.Factory.getVolumeByAssociatedVolumesConstraint(
volumesWithSameNativeGuid.get(0).getId().toString()));
throw APIException.badRequests.cantDeleteSnapshotExposedByVolume(snapshot.getLabel().toString(),
vplexVolumes.get(0).getLabel());
}
}
}
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public void unlinkTargetVolumesFromSnapshotSession(BlockObject snapSessionSourceObj, BlockSnapshotSession snapSession,
Map<URI, Boolean> snapshotDeletionMap, OperationTypeEnum opType, String taskId) {
if (URIUtil.isType(snapSessionSourceObj.getId(), Volume.class)) {
// Get the platform specific implementation for the source side
// backend storage system and call the unlink target method.
Volume vplexVolume = (Volume) snapSessionSourceObj;
BlockObject srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
snapSessionImpl.unlinkTargetVolumesFromSnapshotSession(srcSideBackendVolume, snapSession, snapshotDeletionMap, opType, taskId);
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public void validateRestoreSnapshotSession(List<BlockObject> snapSessionSourceObjs, Project project) {
if (URIUtil.isType(snapSessionSourceObjs.get(0).getId(), Volume.class)) {
// Get the platform specific implementation for the source side
// backend storage system and call the validation routine.
List<BlockObject> srcSideBackendVolumes = new ArrayList<>();
for (BlockObject snapSessionSourceObj : snapSessionSourceObjs) {
Volume vplexVolume = (Volume) snapSessionSourceObj;
srcSideBackendVolumes.add(VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient));
}
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(
srcSideBackendVolumes.get(0).getStorageController());
snapSessionImpl.validateRestoreSnapshotSession(srcSideBackendVolumes, project);
for (BlockObject snapSessionSourceObj : snapSessionSourceObjs) {
Volume vplexVolume = (Volume) snapSessionSourceObj;
// Check for pending tasks on the VPLEX source volume.
checkForPendingTasks(vplexVolume, vplexVolume.getTenant().getURI());
// Verify no active mirrors on the VPLEX volume.
verifyActiveMirrors(vplexVolume);
}
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public void restoreSnapshotSession(BlockSnapshotSession snapSession, BlockObject snapSessionSourceObj, String taskId) {
// Because the source is a VPLEX volume, when the native array snapshot is restored, the
// data on the source side backend volume will be restored to the data on the backend array
// snapshot. This means we have to perform operations on the VPLEX volume to ensure it
// recognizes that the data has been changed.
if (URIUtil.isType(snapSessionSourceObj.getId(), Volume.class)) {
// Invoke VLPEX controller.
Volume vplexVolume = (Volume) snapSessionSourceObj;
URI vplexURI = vplexVolume.getStorageController();
VPlexController controller = getController(VPlexController.class,
DiscoveredDataObject.Type.vplex.toString());
controller.restoreSnapshotSession(vplexURI, snapSession.getId(), taskId);
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public void validateDeleteSnapshotSession(BlockSnapshotSession snapSession, BlockObject snapSessionSourceObj, Project project) {
if (URIUtil.isType(snapSessionSourceObj.getId(), Volume.class)) {
// Get the platform specific implementation for the source side
// backend storage system and call the validation routine.
Volume vplexVolume = (Volume) snapSessionSourceObj;
BlockObject srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
snapSessionImpl.validateDeleteSnapshotSession(snapSession, srcSideBackendVolume, project);
// Check for pending tasks on the VPLEX source volume.
checkForPendingTasks(snapSession, vplexVolume.getTenant().getURI());
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public void deleteSnapshotSession(BlockSnapshotSession snapSession, BlockObject snapSessionSourceObj, String taskId,
String deleteType) {
if (URIUtil.isType(snapSessionSourceObj.getId(), Volume.class)) {
// Get the platform specific implementation for the source side
// backend storage system and call the delete method.
Volume vplexVolume = (Volume) snapSessionSourceObj;
BlockObject srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
snapSessionImpl.deleteSnapshotSession(snapSession, srcSideBackendVolume, taskId, deleteType);
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should never be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
}
/**
* {@inheritDoc}
*/
@Override
public List<BlockSnapshotSession> getSnapshotSessionsForSource(BlockObject sourceObj) {
List<BlockSnapshotSession> snapSessions;
if (URIUtil.isType(sourceObj.getId(), Volume.class)) {
Volume vplexVolume = (Volume) sourceObj;
Volume srcSideBackendVolume = null;
try {
srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
} catch (Exception e) {
// Just log a warning and return the empty list.
s_logger.warn("Cound not find source side backend volume for VPLEX volume {}", vplexVolume.getId());
return new ArrayList<BlockSnapshotSession>();
}
if (srcSideBackendVolume != null) {
URI parentURI = srcSideBackendVolume.getId();
snapSessions = CustomQueryUtility.queryActiveResourcesByConstraint(_dbClient, BlockSnapshotSession.class,
ContainmentConstraint.Factory.getParentSnapshotSessionConstraint(parentURI));
} else {
s_logger.warn("Cound not find source side backend volume for VPLEX volume {}", vplexVolume.getId());
return new ArrayList<BlockSnapshotSession>();
}
} else {
// We don't currently support snaps of BlockSnapshot instances
// so should not be called.
throw APIException.methodNotAllowed.notSupportedForVplexVolumes();
}
return snapSessions;
}
/**
* {@inheritDoc}
*/
@Override
public BlockSnapshotSession prepareSnapshotSessionFromSource(BlockObject sourceObj, String snapSessionLabel, String instanceLabel,
String taskId, boolean inApplication) {
// The snapshot is generally prepared with information from the
// source side backend volume, which is the volume being snapped.
// The passed source object will be a volume, else would not have
// made it this far.
Volume srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume((Volume) sourceObj, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
BlockSnapshotSession snapSession = snapSessionImpl.prepareSnapshotSessionFromSource(srcSideBackendVolume, snapSessionLabel,
instanceLabel, taskId, inApplication);
// However, the project is from the VPLEX volume.
Project sourceProject = BlockSnapshotSessionUtils.querySnapshotSessionSourceProject(sourceObj, _dbClient);
snapSession.setProject(new NamedURI(sourceProject.getId(), sourceObj.getLabel()));
return snapSession;
}
/**
* {@inheritDoc}
*/
@Override
public BlockSnapshot prepareSnapshotForSession(BlockObject sourceObj, String snapsetLabel, String instanceLabel) {
// The snapshot is generally prepared with information from the
// source side backend volume, which is the volume being snapped.
// The passed source object will be a volume, else would not have
// made it this far.
Volume srcSideBackendVolume = VPlexUtil.getVPLEXBackendVolume((Volume) sourceObj, true, _dbClient);
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolume.getStorageController());
BlockSnapshot snapshot = snapSessionImpl.prepareSnapshotForSession(srcSideBackendVolume, snapsetLabel, instanceLabel);
// However, the project is from the VPLEX volume.
Project sourceProject = BlockSnapshotSessionUtils.querySnapshotSessionSourceProject(sourceObj, _dbClient);
snapshot.setProject(new NamedURI(sourceProject.getId(), sourceObj.getLabel()));
_dbClient.updateObject(snapshot);
return snapshot;
}
/**
* {@inheritDoc}
*/
@Override
public List<Map<URI, BlockSnapshot>> prepareSnapshotsForSession(List<BlockObject> sourceObjList, int sourceCount, int newTargetCount,
String newTargetsName, boolean inApplication) {
// The snapshots are generally prepared with information from the
// source side backend volume, which is the volume being snapped.
// The passed source object will be a volume, else would not have
// made it this far.
List<BlockObject> srcSideBackendVolumes = new ArrayList<>();
for (BlockObject sourceObj : sourceObjList) {
srcSideBackendVolumes.add(VPlexUtil.getVPLEXBackendVolume((Volume) sourceObj, true, _dbClient));
}
BlockSnapshotSessionApi snapSessionImpl = getImplementationForBackendSystem(srcSideBackendVolumes.get(0).getStorageController());
List<Map<URI, BlockSnapshot>> snapshotMap = snapSessionImpl.prepareSnapshotsForSession(srcSideBackendVolumes, sourceCount,
newTargetCount, newTargetsName, inApplication);
// However, the project is from the VPLEX volume.
Project sourceProject = BlockSnapshotSessionUtils.querySnapshotSessionSourceProject(sourceObjList.get(0), _dbClient);
for (Map<URI, BlockSnapshot> snapshots : snapshotMap) {
for (BlockSnapshot snapshot : snapshots.values()) {
snapshot.setProject(new NamedURI(sourceProject.getId(), sourceObjList.get(0).getLabel()));
_dbClient.updateObject(snapshot);
}
}
return snapshotMap;
}
/**
* {@inheritDoc}
*/
@Override
public void verifyActiveMirrors(Volume sourceVolume) {
// Check for VPLEX mirrors.
List<URI> activeMirrorsForSource = BlockServiceUtils.getActiveMirrorsForVplexVolume(sourceVolume, _dbClient);
if (!activeMirrorsForSource.isEmpty()) {
throw APIException.badRequests.snapshotSessionSourceHasActiveMirrors(
sourceVolume.getLabel(), activeMirrorsForSource.size());
}
}
}