/*
* Copyright (c) 2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource.snapshot;
import static com.emc.storageos.api.mapper.BlockMapper.map;
import static com.emc.storageos.api.mapper.DbObjectMapper.toNamedRelatedResource;
import static com.emc.storageos.api.mapper.TaskMapper.toTask;
import static com.emc.storageos.db.client.util.CommonTransformerFunctions.fctnDataObjectToID;
import static com.emc.storageos.db.client.util.NullColumnValueGetter.isNullURI;
import static com.google.common.collect.Collections2.transform;
import static java.lang.String.format;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
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.Map.Entry;
import java.util.Set;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
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.mapper.TaskMapper;
import com.emc.storageos.api.service.authorization.PermissionsHelper;
import com.emc.storageos.api.service.impl.resource.BlockService;
import com.emc.storageos.api.service.impl.resource.ResourceService;
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.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.BlockSnapshotSession;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.client.model.DiscoveredDataObject;
import com.emc.storageos.db.client.model.NamedURI;
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.VirtualPool;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.client.model.util.BlockConsistencyGroupUtils;
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.BlockSnapshotSessionList;
import com.emc.storageos.model.block.BlockSnapshotSessionRestRep;
import com.emc.storageos.model.block.SnapshotSessionCreateParam;
import com.emc.storageos.model.block.SnapshotSessionLinkTargetsParam;
import com.emc.storageos.model.block.SnapshotSessionNewTargetsParam;
import com.emc.storageos.model.block.SnapshotSessionRelinkTargetsParam;
import com.emc.storageos.model.block.SnapshotSessionUnlinkTargetParam;
import com.emc.storageos.model.block.SnapshotSessionUnlinkTargetsParam;
import com.emc.storageos.model.block.VolumeDeleteTypeEnum;
import com.emc.storageos.security.audit.AuditLogManager;
import com.emc.storageos.security.authentication.InterNodeHMACAuthFilter;
import com.emc.storageos.security.authentication.StorageOSUser;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.services.util.TimeUtils;
import com.emc.storageos.svcs.errorhandling.model.ServiceCoded;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.svcs.errorhandling.resources.InternalException;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;
/**
* Class that implements all block snapshot session requests.
*/
public class BlockSnapshotSessionManager {
// Enumeration specifying the valid keys for the snapshot session implementations map.
public enum SnapshotSessionImpl {
dflt, vmax, vmax3, vnx, vnxe, hds, openstack, scaleio, xtremio, xiv, rp, vplex
}
// A reference to a database client.
private final DbClient _dbClient;
// A reference to a permissions helper.
private PermissionsHelper _permissionsHelper = null;
// A reference to the audit log manager.
private AuditLogManager _auditLogManager = null;
// A reference to the security context
private final SecurityContext _securityContext;
// A reference to the snapshot session request.
protected HttpServletRequest _request;
// A reference to the URI information.
private final UriInfo _uriInfo;
// The supported block snapshot session API implementations
private final Map<String, BlockSnapshotSessionApi> _snapshotSessionImpls = new HashMap<String, BlockSnapshotSessionApi>();
// A reference to a logger.
private static final Logger s_logger = LoggerFactory.getLogger(BlockSnapshotSessionManager.class);
/**
* Constructor
*
* @param dbClient A reference to a database client.
* @param permissionsHelper A reference to a permission helper.
* @param auditLogManager A reference to an audit log manager.
* @param coordinator A reference to the coordinator.
* @param securityContext A reference to the security context.
* @param uriInfo A reference to the URI info.
* @param request A reference to the snapshot session request.
*/
public BlockSnapshotSessionManager(DbClient dbClient, PermissionsHelper permissionsHelper,
AuditLogManager auditLogManager, CoordinatorClient coordinator,
SecurityContext securityContext, UriInfo uriInfo, HttpServletRequest request) {
_dbClient = dbClient;
_permissionsHelper = permissionsHelper;
_auditLogManager = auditLogManager;
_securityContext = securityContext;
_uriInfo = uriInfo;
_request = request;
// Create snapshot session implementations.
createPlatformSpecificImpls(coordinator);
}
/**
* Create all platform specific snapshot session implementations.
*
* @param coordinator A reference to the coordinator.
*/
private void createPlatformSpecificImpls(CoordinatorClient coordinator) {
_snapshotSessionImpls.put(SnapshotSessionImpl.dflt.name(), new DefaultBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.vmax.name(), new VMAXBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.vmax3.name(), new VMAX3BlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.vnx.name(), new VNXBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.vnxe.name(), new VNXEBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.hds.name(), new HDSBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.openstack.name(), new OpenstackBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.scaleio.name(), new ScaleIOBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.xtremio.name(), new XtremIOBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.xiv.name(), new XIVBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.vplex.name(), new VPlexBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
_snapshotSessionImpls.put(SnapshotSessionImpl.rp.name(), new RPBlockSnapshotSessionApiImpl(_dbClient, coordinator,
_permissionsHelper, _securityContext, this));
}
/**
* Gets a specific platform implementation.
*
* @param implType The specific implementation desired.
*
* @return The platform specific snapshot session implementation.
*/
public BlockSnapshotSessionApi getPlatformSpecificImplOfType(SnapshotSessionImpl implType) {
return _snapshotSessionImpls.get(implType.name());
}
/**
* Get the platform implementation for the passed system.
*
* @param system A reference to the storage system.
*
* @return A reference to the platform implementation for the passed system.
*/
public BlockSnapshotSessionApi getPlatformSpecificImplForSystem(StorageSystem system) {
BlockSnapshotSessionApi snapSessionApi = null;
String systemType = system.getSystemType();
if (DiscoveredDataObject.Type.vmax.name().equals(systemType)) {
if (system.checkIfVmax3()) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.vmax3.name());
} else {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.vmax.name());
}
} else if (DiscoveredDataObject.Type.vnxblock.name().equals(systemType)) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.vnx.name());
} else if (DiscoveredDataObject.Type.vnxe.name().equals(systemType)) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.vnxe.name());
} else if (DiscoveredDataObject.Type.hds.name().equals(systemType)) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.hds.name());
} else if (DiscoveredDataObject.Type.openstack.name().equals(systemType)) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.openstack.name());
} else if (DiscoveredDataObject.Type.scaleio.name().equals(systemType)) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.scaleio.name());
} else if (DiscoveredDataObject.Type.xtremio.name().equals(systemType)) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.xtremio.name());
} else if (DiscoveredDataObject.Type.ibmxiv.name().equals(systemType)) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.xiv.name());
} else if (DiscoveredDataObject.Type.vplex.name().equals(systemType)) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.vplex.name());
} else {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.dflt.name());
}
return snapSessionApi;
}
/**
* Creates a snapshot session based on the given resource URI. This method handles the following cases where
* resourceURI is...
*
* 1) a non-CG Volume/BlockSnapshot
* 2) a BlockConsistencyGroup
* 3) a CG Volume/BlockSnapshot (recursively calls this method, passing in its BlockConsistencyGroup URI)
*
* @param resourceURI Resource to create a snapshot session from
* @param param Snapshot session parameters
* @param fcManager Full copy manager
* @return TaskList
*/
public TaskList createSnapshotSession(URI resourceURI, SnapshotSessionCreateParam param, BlockFullCopyManager fcManager) {
if (URIUtil.isType(resourceURI, Volume.class) || URIUtil.isType(resourceURI, BlockSnapshot.class)) {
BlockObject blockObject = BlockSnapshotSessionUtils.querySnapshotSessionSource(resourceURI, _uriInfo, false, _dbClient);
if (NullColumnValueGetter.isNotNullValue(blockObject.getReplicationGroupInstance())) {
return createSnapshotSession(blockObject.getConsistencyGroup(), param, fcManager);
} else {
return createSnapshotSession(Lists.newArrayList(blockObject), param, fcManager);
}
} else if (URIUtil.isType(resourceURI, BlockConsistencyGroup.class)) {
BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, resourceURI);
return createSnapshotSession(cg, param, fcManager);
}
return null;
}
@SuppressWarnings("unchecked")
public TaskList createSnapshotSession(BlockConsistencyGroup cg, SnapshotSessionCreateParam param, BlockFullCopyManager fcManager) {
Table<URI, String, List<Volume>> storageRgToVolumes = null;
if (!param.getVolumes().isEmpty()) {
// volume group snapshot session
// group volumes by backend storage system and replication group
storageRgToVolumes = BlockServiceUtils.getReplicationGroupVolumes(param.getVolumes(), cg.getId(), _dbClient, _uriInfo);
} else {
// CG snapshot session
storageRgToVolumes = BlockServiceUtils.getReplicationGroupVolumes(
BlockConsistencyGroupUtils.getAllCGVolumes(cg, _dbClient), _dbClient);
}
TaskList taskList = new TaskList();
for (Cell<URI, String, List<Volume>> cell : storageRgToVolumes.cellSet()) {
String rgName = cell.getColumnKey();
List<Volume> volumeList = cell.getValue();
s_logger.info("Processing Replication Group {}, Volumes {}",
rgName, Joiner.on(',').join(transform(volumeList, fctnDataObjectToID())));
if (volumeList == null || volumeList.isEmpty()) {
s_logger.warn(String.format("No volume in replication group %s", rgName));
continue;
}
try {
taskList.getTaskList().addAll(
createSnapshotSession(((List<BlockObject>) (List<?>) volumeList), param, fcManager).getTaskList());
} catch (InternalException | APIException e) {
s_logger.error("Exception when creating snapshot session for replication group {}", rgName, e);
TaskResourceRep task = BlockServiceUtils.createFailedTaskOnCG(_dbClient, cg,
ResourceOperationTypeEnum.CREATE_CONSISTENCY_GROUP_SNAPSHOT_SESSION, e);
taskList.addTask(task);
} catch (Exception ex) {
s_logger.error("Unexpected Exception occurred when creating snapshot session for replication group {}",
rgName, ex);
}
}
return taskList;
}
/**
* Implements a request to create a new block snapshot session.
*
* @param snapSessionSourceObjList The URI of the snapshot session source object.
* @param param A reference to the create session information.
* @param fcManager A reference to a full copy manager.
*
* @return TaskList A TaskList
*/
public TaskList createSnapshotSession(List<BlockObject> snapSessionSourceObjList,
SnapshotSessionCreateParam param, BlockFullCopyManager fcManager) {
Collection<URI> sourceURIs = transform(snapSessionSourceObjList, fctnDataObjectToID());
s_logger.info("START create snapshot session for sources {}", Joiner.on(',').join(sourceURIs));
// Get the snapshot session label.
String snapSessionLabel = TimeUtils.formatDateForCurrent(param.getName());
// Get the target device information, if any.
int newLinkedTargetsCount = 0;
String newTargetsName = null;
String newTargetsCopyMode = BlockSnapshot.CopyMode.nocopy.name();
SnapshotSessionNewTargetsParam linkedTargetsParam = param.getNewLinkedTargets();
if (linkedTargetsParam != null) {
newLinkedTargetsCount = linkedTargetsParam.getCount().intValue();
newTargetsName = TimeUtils.formatDateForCurrent(linkedTargetsParam.getTargetName());
newTargetsCopyMode = linkedTargetsParam.getCopyMode();
}
BlockObject sourceObj = snapSessionSourceObjList.get(0);
// Get the project for the snapshot session source object.
Project project = BlockSnapshotSessionUtils.querySnapshotSessionSourceProject(sourceObj, _dbClient);
// Get the platform specific block snapshot session implementation.
BlockSnapshotSessionApi snapSessionApiImpl = determinePlatformSpecificImplForSource(sourceObj);
// Validate the create snapshot session request.
snapSessionApiImpl.validateSnapshotSessionCreateRequest(sourceObj, snapSessionSourceObjList, project, snapSessionLabel,
newLinkedTargetsCount, newTargetsName, newTargetsCopyMode, false, fcManager);
// Create a unique task identifier.
String taskId = UUID.randomUUID().toString();
boolean inApplication = false;
if (sourceObj instanceof Volume && ((Volume) sourceObj).getApplication(_dbClient) != null) {
inApplication = true;
} else if (sourceObj instanceof BlockSnapshot) {
BlockSnapshot sourceSnap = (BlockSnapshot) sourceObj;
NamedURI namedUri = sourceSnap.getParent();
if (!NullColumnValueGetter.isNullNamedURI(namedUri)) {
Volume source = _dbClient.queryObject(Volume.class, namedUri.getURI());
if (source != null && source.getApplication(_dbClient) != null) {
inApplication = true;
}
}
}
// Prepare the ViPR BlockSnapshotSession instances and BlockSnapshot
// instances for any new targets to be created and linked to the
// snapshot sessions.
List<Map<URI, BlockSnapshot>> snapSessionSnapshots = new ArrayList<>();
BlockSnapshotSession snapSession = snapSessionApiImpl.prepareSnapshotSession(snapSessionSourceObjList,
snapSessionLabel, newLinkedTargetsCount, newTargetsName, snapSessionSnapshots, taskId, inApplication);
// Populate the preparedObjects list and create tasks for each snapshot session.
TaskList response = new TaskList();
Operation snapSessionOp = _dbClient.createTaskOpStatus(BlockSnapshotSession.class, snapSession.getId(),
taskId, getCreateResourceOperationTypeEnum(snapSession));
snapSession.getOpStatus().put(taskId, snapSessionOp);
response.getTaskList().add(toTask(snapSession, taskId, snapSessionOp));
if (snapSession.hasConsistencyGroup()) {
addConsistencyGroupTasks(snapSessionSourceObjList, response, taskId,
getCreateResourceOperationTypeEnum(snapSession));
} else {
for (BlockObject sourceForTask : snapSessionSourceObjList) {
@SuppressWarnings("unchecked")
Operation op = _dbClient.createTaskOpStatus(URIUtil.getModelClass(sourceForTask.getId()),
sourceForTask.getId(), taskId, ResourceOperationTypeEnum.CREATE_SNAPSHOT_SESSION);
response.getTaskList().add(toTask(sourceForTask, taskId, op));
}
}
List<DataObject> preparedObjects = new ArrayList<>();
List<List<URI>> snapSessionSnapshotURIs = new ArrayList<>();
for (Map<URI, BlockSnapshot> snapshotMap : snapSessionSnapshots) {
// Set Copy Mode
for (Entry<URI, BlockSnapshot> entry : snapshotMap.entrySet()) {
entry.getValue().setCopyMode(newTargetsCopyMode);
}
preparedObjects.addAll(snapshotMap.values());
Set<URI> uris = snapshotMap.keySet();
snapSessionSnapshotURIs.add(Lists.newArrayList(uris));
}
// persist copyMode changes
_dbClient.updateObject(preparedObjects);
preparedObjects.add(snapSession);
// Create the snapshot sessions.
try {
snapSessionApiImpl.createSnapshotSession(sourceObj, snapSession.getId(),
snapSessionSnapshotURIs, newTargetsCopyMode, taskId);
} catch (Exception e) {
String errorMsg = format("Failed to create snapshot sessions for source %s: %s", sourceObj.getId(), e.getMessage());
ServiceCoded sc = null;
if (e instanceof ServiceCoded) {
sc = (ServiceCoded) e;
} else {
sc = APIException.internalServerErrors.genericApisvcError(errorMsg, e);
}
cleanupFailure(response.getTaskList(), preparedObjects, errorMsg, taskId, sc);
throw e;
}
// Record a message in the audit log.
auditOp(OperationTypeEnum.CREATE_SNAPSHOT_SESSION, true, AuditLogManager.AUDITOP_BEGIN,
snapSessionLabel, sourceObj.getId().toString(), sourceObj.getStorageController().toString());
s_logger.info("FINISH create snapshot session for source {}", sourceObj.getId());
return response;
}
/**
* Implements a request to create and link new target volumes to the
* BlockSnapshotSession instance with the passed URI.
*
* @param snapSessionURI The URI of a BlockSnapshotSession instance.
* @param param The linked target information.
*
* @return A TaskResourceRep.
*/
public TaskList linkTargetVolumesToSnapshotSession(URI snapSessionURI, SnapshotSessionLinkTargetsParam param) {
s_logger.info("START link new targets for snapshot session {}", snapSessionURI);
// Get the snapshot session.
BlockSnapshotSession snapSession = BlockSnapshotSessionUtils.querySnapshotSession(snapSessionURI, _uriInfo, _dbClient, true);
BlockObject snapSessionSourceObj = null;
List<BlockObject> snapSessionSourceObjs = getAllSnapshotSessionSources(snapSession);
snapSessionSourceObj = snapSessionSourceObjs.get(0);
// Get the project for the snapshot session source object.
Project project = BlockSnapshotSessionUtils.querySnapshotSessionSourceProject(snapSessionSourceObj, _dbClient);
BlockSnapshotSessionApi snapSessionApiImpl = determinePlatformSpecificImplForSource(snapSessionSourceObj);
boolean inApplication = false;
if (snapSessionSourceObj instanceof Volume && ((Volume) snapSessionSourceObj).getApplication(_dbClient) != null) {
inApplication = true;
} else if (snapSessionSourceObj instanceof BlockSnapshot) {
BlockSnapshot sourceSnap = (BlockSnapshot) snapSessionSourceObj;
NamedURI namedUri = sourceSnap.getParent();
if (!NullColumnValueGetter.isNullNamedURI(namedUri)) {
Volume source = _dbClient.queryObject(Volume.class, namedUri.getURI());
if (source != null && source.getApplication(_dbClient) != null) {
inApplication = true;
}
}
}
// Get the target information.
int newLinkedTargetsCount = param.getNewLinkedTargets().getCount();
String newTargetsName = param.getNewLinkedTargets().getTargetName();
String newTargetsCopyMode = param.getNewLinkedTargets().getCopyMode();
if (newTargetsCopyMode == null) {
newTargetsCopyMode = BlockSnapshot.CopyMode.nocopy.name();
}
// Validate that the requested new targets can be linked to the snapshot session.
snapSessionApiImpl.validateLinkNewTargetsRequest(snapSessionSourceObj, project, newLinkedTargetsCount, newTargetsName,
newTargetsCopyMode);
// Prepare the BlockSnapshot instances to represent the new linked targets.
List<Map<URI, BlockSnapshot>> snapshots = snapSessionApiImpl.prepareSnapshotsForSession(snapSessionSourceObjs, 0,
newLinkedTargetsCount,
newTargetsName,
inApplication);
// Create a unique task identifier.
String taskId = UUID.randomUUID().toString();
TaskList response = new TaskList();
List<DataObject> preparedObjects = new ArrayList<>();
// Create a task for the snapshot session.
Operation op = new Operation();
op.setResourceType(ResourceOperationTypeEnum.LINK_SNAPSHOT_SESSION_TARGETS);
_dbClient.createTaskOpStatus(BlockSnapshotSession.class, snapSessionURI, taskId, op);
snapSession.getOpStatus().put(taskId, op);
response.getTaskList().add(toTask(snapSession, taskId));
List<List<URI>> snapSessionSnapshotURIs = new ArrayList<>();
for (Map<URI, BlockSnapshot> snapshotMap : snapshots) {
// Set Copy Mode
for (Entry<URI, BlockSnapshot> entry : snapshotMap.entrySet()) {
entry.getValue().setCopyMode(newTargetsCopyMode);
}
preparedObjects.addAll(snapshotMap.values());
Set<URI> uris = snapshotMap.keySet();
snapSessionSnapshotURIs.add(Lists.newArrayList(uris));
}
// persist copyMode changes
_dbClient.updateObject(preparedObjects);
// Create and link new targets to the snapshot session.
try {
snapSessionApiImpl.linkNewTargetVolumesToSnapshotSession(snapSessionSourceObj, snapSession,
snapSessionSnapshotURIs, newTargetsCopyMode, taskId);
} catch (Exception e) {
String errorMsg = format("Failed to link new targets for snapshot session %s: %s", snapSessionURI, e.getMessage());
ServiceCoded sc = null;
if (e instanceof ServiceCoded) {
sc = (ServiceCoded) e;
} else {
sc = APIException.internalServerErrors.genericApisvcError(errorMsg, e);
}
cleanupFailure(response.getTaskList(), preparedObjects, errorMsg, taskId, sc);
throw e;
}
// Create the audit log entry.
auditOp(OperationTypeEnum.LINK_SNAPSHOT_SESSION_TARGET, true, AuditLogManager.AUDITOP_BEGIN,
snapSessionURI.toString(), snapSessionSourceObj.getId().toString(), snapSessionSourceObj.getStorageController().toString());
s_logger.info("FINISH link new targets for snapshot session {}", snapSessionURI);
return response;
}
/**
* Implements a request to relink the passed targets from the
* BlockSnapshotSession instance with the passed URI.
*
* @param snapSessionURI The URI of a BlockSnapshotSession instance.
* @param param The linked target information.
*
* @return A TaskList.
*/
public TaskList relinkTargetVolumesToSnapshotSession(URI snapSessionURI, SnapshotSessionRelinkTargetsParam param) {
s_logger.info("START relink targets to snapshot session {}", snapSessionURI);
// Get the snapshot session.
BlockSnapshotSession snapSession = BlockSnapshotSessionUtils.querySnapshotSession(snapSessionURI, _uriInfo, _dbClient, true);
BlockObject snapSessionSourceObj = null;
List<BlockObject> snapSessionSourceObjs = getAllSnapshotSessionSources(snapSession);
snapSessionSourceObj = snapSessionSourceObjs.get(0);
// Get the project for the snapshot session source object.
Project project = BlockSnapshotSessionUtils.querySnapshotSessionSourceProject(snapSessionSourceObj, _dbClient);
// Get the platform specific block snapshot session implementation.
BlockSnapshotSessionApi snapSessionApiImpl = determinePlatformSpecificImplForSource(snapSessionSourceObj);
// Get the target information.
List<URI> linkedTargetURIs = param.getLinkedTargetIds();
// Validate that the requested targets can be re-linked to the snapshot session.
snapSessionApiImpl.validateRelinkSnapshotSessionTargets(snapSessionSourceObj, snapSession, project, linkedTargetURIs, _uriInfo);
// Create a unique task identifier.
String taskId = UUID.randomUUID().toString();
// Create a task for the snapshot session.
Operation op = new Operation();
op.setResourceType(getRelinkResourceOperationTypeEnum(snapSession));
_dbClient.createTaskOpStatus(BlockSnapshotSession.class, snapSessionURI, taskId, op);
snapSession.getOpStatus().put(taskId, op);
TaskResourceRep response = toTask(snapSession, taskId);
TaskList taskList = new TaskList();
taskList.addTask(response);
// Re-link the targets to the snapshot session.
try {
snapSessionApiImpl.relinkTargetVolumesToSnapshotSession(snapSessionSourceObj, snapSession, linkedTargetURIs, taskId);
} catch (Exception e) {
String errorMsg = format("Failed to relink targets to snapshot session %s: %s", snapSessionURI, e.getMessage());
ServiceCoded sc = null;
if (e instanceof ServiceCoded) {
sc = (ServiceCoded) e;
} else {
sc = APIException.internalServerErrors.genericApisvcError(errorMsg, e);
}
cleanupFailure(Arrays.asList(response), new ArrayList<DataObject>(), errorMsg, taskId, sc);
throw e;
}
// Create the audit log entry.
auditOp(OperationTypeEnum.RELINK_SNAPSHOT_SESSION_TARGET, true, AuditLogManager.AUDITOP_BEGIN,
snapSessionURI.toString(), snapSessionSourceObj.getId().toString(), snapSessionSourceObj.getStorageController().toString());
s_logger.info("FINISH relink targets to snapshot session {}", snapSessionURI);
return taskList;
}
/**
* Implements a request to unlink the passed targets from the
* BlockSnapshotSession instance with the passed URI.
*
* @param snapSessionURI The URI of a BlockSnapshotSession instance.
* @param param The linked target information.
*
* @return A TaskResourceRep.
*/
public TaskResourceRep unlinkTargetVolumesFromSnapshotSession(URI snapSessionURI, SnapshotSessionUnlinkTargetsParam param) {
return unlinkTargetVolumesFromSnapshotSession(snapSessionURI, param, OperationTypeEnum.UNLINK_SNAPSHOT_SESSION_TARGET);
}
/**
* Implements a request to unlink the passed targets from the
* BlockSnapshotSession instance with the passed URI.
*
* @param snapSessionURI The URI of a BlockSnapshotSession instance.
* @param param The linked target information.
* @param opType The operation type for the audit and event logs.
*
* @return A TaskResourceRep.
*/
public TaskResourceRep unlinkTargetVolumesFromSnapshotSession(URI snapSessionURI, SnapshotSessionUnlinkTargetsParam param,
OperationTypeEnum opType) {
s_logger.info("START unlink targets from snapshot session {}", snapSessionURI);
// Get the snapshot session.
BlockSnapshotSession snapSession = BlockSnapshotSessionUtils.querySnapshotSession(snapSessionURI, _uriInfo, _dbClient, true);
BlockObject snapSessionSourceObj = null;
List<BlockObject> snapSessionSourceObjs = getAllSnapshotSessionSources(snapSession);
snapSessionSourceObj = snapSessionSourceObjs.get(0);
// Get the project for the snapshot session source object.
Project project = BlockSnapshotSessionUtils.querySnapshotSessionSourceProject(snapSessionSourceObj, _dbClient);
BlockSnapshotSessionApi snapSessionApiImpl = determinePlatformSpecificImplForSource(snapSessionSourceObj);
// Get the target information.
Map<URI, Boolean> targetMap = new HashMap<>();
for (SnapshotSessionUnlinkTargetParam targetInfo : param.getLinkedTargets()) {
URI targetURI = targetInfo.getId();
Boolean deleteTarget = targetInfo.getDeleteTarget();
if (deleteTarget == null) {
deleteTarget = Boolean.FALSE;
}
targetMap.put(targetURI, deleteTarget);
}
// Validate that the requested targets can be unlinked from the snapshot session.
snapSessionApiImpl.validateUnlinkSnapshotSessionTargets(snapSession, snapSessionSourceObj, project, targetMap, _uriInfo);
// Create a unique task identifier.
String taskId = UUID.randomUUID().toString();
// Create a task for the snapshot session.
Operation op = new Operation();
op.setResourceType(ResourceOperationTypeEnum.UNLINK_SNAPSHOT_SESSION_TARGETS);
_dbClient.createTaskOpStatus(BlockSnapshotSession.class, snapSessionURI, taskId, op);
snapSession.getOpStatus().put(taskId, op);
TaskResourceRep response = toTask(snapSession, taskId);
// Unlink the targets from the snapshot session.
try {
snapSessionApiImpl.unlinkTargetVolumesFromSnapshotSession(snapSessionSourceObj, snapSession, targetMap, opType, taskId);
} catch (Exception e) {
String errorMsg = format("Failed to unlink targets from snapshot session %s: %s", snapSessionURI, e.getMessage());
ServiceCoded sc = null;
if (e instanceof ServiceCoded) {
sc = (ServiceCoded) e;
} else {
sc = APIException.internalServerErrors.genericApisvcError(errorMsg, e);
}
cleanupFailure(Arrays.asList(response), new ArrayList<DataObject>(), errorMsg, taskId, sc);
throw e;
}
// Create the audit log entry.
auditOp(opType, true, AuditLogManager.AUDITOP_BEGIN, snapSessionURI.toString(), snapSessionSourceObj.getId().toString(),
snapSessionSourceObj.getStorageController().toString());
s_logger.info("FINISH unlink targets from snapshot session {}", snapSessionURI);
return response;
}
/**
* Restores the data on the array snapshot point-in-time copy represented by the
* BlockSnapshotSession instance with the passed URI, to the snapshot session source
* object.
*
* @param snapSessionURI The URI of the BlockSnapshotSession instance to be restored.
*
* @return TaskResourceRep representing the snapshot session task.
*/
public TaskResourceRep restoreSnapshotSession(URI snapSessionURI) {
s_logger.info("START restore snapshot session {}", snapSessionURI);
// Get the snapshot session.
BlockSnapshotSession snapSession = BlockSnapshotSessionUtils.querySnapshotSession(snapSessionURI, _uriInfo, _dbClient, true);
BlockObject snapSessionSourceObj = null;
List<BlockObject> snapSessionSourceObjs = getAllSnapshotSessionSources(snapSession);
snapSessionSourceObj = snapSessionSourceObjs.get(0);
// Get the project for the snapshot session source object.
Project project = BlockSnapshotSessionUtils.querySnapshotSessionSourceProject(snapSessionSourceObj, _dbClient);
BlockSnapshotSessionApi snapSessionApiImpl = determinePlatformSpecificImplForSource(snapSessionSourceObj);
// Validate that the snapshot session can be restored.
snapSessionApiImpl.validateRestoreSnapshotSession(snapSessionSourceObjs, project);
// Create the task identifier.
String taskId = UUID.randomUUID().toString();
// Create the operation status entry in the status map for the snapshot.
Operation op = new Operation();
op.setResourceType(ResourceOperationTypeEnum.RESTORE_SNAPSHOT_SESSION);
_dbClient.createTaskOpStatus(BlockSnapshotSession.class, snapSessionURI, taskId, op);
snapSession.getOpStatus().put(taskId, op);
TaskResourceRep resourceRep = toTask(snapSession, taskId);
// Restore the snapshot session.
try {
snapSessionApiImpl.restoreSnapshotSession(snapSession, snapSessionSourceObjs.get(0), taskId);
} catch (Exception e) {
String errorMsg = format("Failed to restore snapshot session %s: %s", snapSessionURI, e.getMessage());
ServiceCoded sc = null;
if (e instanceof ServiceCoded) {
sc = (ServiceCoded) e;
} else {
sc = APIException.internalServerErrors.genericApisvcError(errorMsg, e);
}
cleanupFailure(Lists.newArrayList(resourceRep), new ArrayList<DataObject>(), errorMsg, taskId, sc);
throw e;
}
// Create the audit log entry.
auditOp(OperationTypeEnum.RESTORE_SNAPSHOT_SESSION, true, AuditLogManager.AUDITOP_BEGIN,
snapSessionURI.toString(), snapSessionSourceObjs.get(0).getId().toString(), snapSessionSourceObjs.get(0)
.getStorageController().toString());
s_logger.info("FINISH restore snapshot session {}", snapSessionURI);
return resourceRep;
}
/**
* Get the details for the BlockSnapshotSession instance with the passed URI.
*
* @param snapSessionURI The URI of the BlockSnapshotSession instance.
*
* @return An instance of BlockSnapshotSessionRestRep with the details for the requested snapshot session.
*/
public BlockSnapshotSessionRestRep getSnapshotSession(URI snapSessionURI) {
BlockSnapshotSession snapSession = BlockSnapshotSessionUtils.querySnapshotSession(snapSessionURI, _uriInfo, _dbClient, false);
return map(_dbClient, snapSession);
}
/**
* Get the BlockSnapshotSession instances for the source object with the passed URI.
*
* @param sourceURI The URI of the snapshot session source object.
*
* @return A BlockSnapshotSessionList of the snapshot sessions for the source.
*/
public BlockSnapshotSessionList getSnapshotSessionsForSource(URI sourceURI) {
// Get the snapshot session source object.
BlockObject sourceObj = BlockSnapshotSessionUtils.querySnapshotSessionSource(sourceURI, _uriInfo, true, _dbClient);
// Get the platform specific block snapshot session implementation.
BlockSnapshotSessionApi snapSessionApiImpl = determinePlatformSpecificImplForSource(sourceObj);
// Get the BlockSnapshotSession instances for the source and prepare the result.
List<BlockSnapshotSession> snapSessionsForSource = snapSessionApiImpl.getSnapshotSessionsForSource(sourceObj);
BlockSnapshotSessionList result = new BlockSnapshotSessionList();
for (BlockSnapshotSession snapSessionForSource : snapSessionsForSource) {
result.getSnapSessionRelatedResourceList().add(toNamedRelatedResource(snapSessionForSource));
}
return result;
}
/**
* @param group
* @return
*/
public BlockSnapshotSessionList getSnapshotSessionsForConsistencyGroup(BlockConsistencyGroup group) {
BlockSnapshotSessionList result = new BlockSnapshotSessionList();
List<Volume> volumes = ControllerUtils.getVolumesPartOfCG(group.getId(), _dbClient);
if (volumes.isEmpty()) {
return result;
}
// if any of the source volumes are in an application, replica management must be done via the application
for (Volume srcVol : volumes) {
if (srcVol.getApplication(_dbClient) != null) {
return result;
}
}
Volume sourceVolume = volumes.get(0);
// Get the platform specific block snapshot session implementation.
BlockSnapshotSessionApi snapSessionApiImpl = determinePlatformSpecificImplForSource(sourceVolume);
// Get the BlockSnapshotSession instances for the source and prepare the result.
List<BlockSnapshotSession> snapSessions = snapSessionApiImpl.getSnapshotSessionsForConsistencyGroup(group);
for (BlockSnapshotSession snapSession : snapSessions) {
result.getSnapSessionRelatedResourceList().add(toNamedRelatedResource(snapSession));
}
return result;
}
/**
* Gets the snapshot sessions for consistency group.
*
* @param group the consistency group
* @return the snapshot sessions for consistency group
*/
public List<BlockSnapshotSession> getSnapshotSessionsForCG(BlockConsistencyGroup group) {
List<Volume> volumes = ControllerUtils.getVolumesPartOfCG(group.getId(), _dbClient);
if (volumes.isEmpty()) {
return Collections.<BlockSnapshotSession> emptyList();
}
Volume sourceVolume = volumes.get(0);
// Get the platform specific block snapshot session implementation.
BlockSnapshotSessionApi snapSessionApiImpl = determinePlatformSpecificImplForSource(sourceVolume);
// Get the BlockSnapshotSession instances for the source.
return snapSessionApiImpl.getSnapshotSessionsForConsistencyGroup(group);
}
/**
* Delete the snapshot session with the passed URI.
*
* @param snapSessionURI The URI of the BlockSnapshotSession instance.
* @param deleteType The deletion type i.e, VIPR_ONLY or FULL
*
* @return TaskResourceRep representing the snapshot session task.
*/
public TaskList deleteSnapshotSession(URI snapSessionURI, String deleteType) {
s_logger.info("START delete snapshot session {} of type {}", snapSessionURI, deleteType);
// Get the snapshot session.
BlockSnapshotSession snapSession = BlockSnapshotSessionUtils.querySnapshotSession(snapSessionURI, _uriInfo, _dbClient, true);
BlockObject snapSessionSourceObj = null;
List<BlockObject> snapSessionSourceObjs = getAllSnapshotSessionSources(snapSession);
snapSessionSourceObj = snapSessionSourceObjs.get(0);
// Get the project for the snapshot session source object.
Project project = BlockSnapshotSessionUtils.querySnapshotSessionSourceProject(snapSessionSourceObj, _dbClient);
BlockSnapshotSessionApi snapSessionApiImpl = determinePlatformSpecificImplForSource(snapSessionSourceObj);
// Validate that the snapshot session can be deleted.
snapSessionApiImpl.validateDeleteSnapshotSession(snapSession, snapSessionSourceObj, project);
// Create the task identifier.
String taskId = UUID.randomUUID().toString();
TaskList taskList = new TaskList();
Operation snapSessionOp = new Operation();
snapSessionOp.setResourceType(getDeleteResourceOperationTypeEnum(snapSession));
_dbClient.createTaskOpStatus(BlockSnapshotSession.class, snapSession.getId(), taskId, snapSessionOp);
snapSession.getOpStatus().put(taskId, snapSessionOp);
taskList.addTask(toTask(snapSession, taskId, snapSessionOp));
if (snapSession.hasConsistencyGroup() && NullColumnValueGetter.isNotNullValue(snapSession.getReplicationGroupInstance())) {
addConsistencyGroupTasks(snapSessionSourceObjs, taskList, taskId, getDeleteResourceOperationTypeEnum(snapSession));
}
// Delete the snapshot session.
try {
snapSessionApiImpl.deleteSnapshotSession(snapSession, snapSessionSourceObj, taskId, deleteType);
} catch (Exception e) {
String errorMsg = format("Failed to delete snapshot session %s: %s", snapSessionURI, e.getMessage());
ServiceCoded sc = null;
if (e instanceof ServiceCoded) {
sc = (ServiceCoded) e;
} else {
sc = APIException.internalServerErrors.genericApisvcError(errorMsg, e);
}
cleanupFailure(taskList.getTaskList(), new ArrayList<DataObject>(), errorMsg, taskId, sc);
throw e;
}
// Create the audit log entry.
String opStage = VolumeDeleteTypeEnum.VIPR_ONLY.name().equals(deleteType) ? null : AuditLogManager.AUDITOP_BEGIN;
auditOp(OperationTypeEnum.DELETE_SNAPSHOT_SESSION, true, opStage, snapSessionURI.toString(),
snapSessionURI.toString(), snapSessionSourceObj.getStorageController().toString());
s_logger.info("FINISH delete snapshot session {}", snapSessionURI);
return taskList;
}
/**
* Determines and returns the platform specific snapshot session implementation.
*
* @param sourceObj A reference to the snapshot session source.
*
* @return The platform specific snapshot session implementation.
*/
private BlockSnapshotSessionApi determinePlatformSpecificImplForSource(BlockObject sourceObj) {
BlockSnapshotSessionApi snapSessionApi = null;
if (BlockObject.checkForRP(_dbClient, sourceObj.getId())) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.rp.name());
} else {
VirtualPool vpool = BlockSnapshotSessionUtils.querySnapshotSessionSourceVPool(sourceObj, _dbClient);
if (VirtualPool.vPoolSpecifiesHighAvailability(vpool) && sourceObj.isVPlexVolume(_dbClient)) {
snapSessionApi = _snapshotSessionImpls.get(SnapshotSessionImpl.vplex.name());
} else {
URI systemURI = sourceObj.getStorageController();
StorageSystem system = _dbClient.queryObject(StorageSystem.class, systemURI);
snapSessionApi = getPlatformSpecificImplForSystem(system);
}
}
return snapSessionApi;
}
/**
* Record audit log for services.
*
* @param opType audit event type (e.g. CREATE_VPOOL|TENANT etc.)
* @param operationalStatus Status of operation (true|false)
* @param operationStage Stage of operation. For sync operation, it should
* be null; For async operation, it should be "BEGIN" or "END";
* @param descparams Description parameters
*/
private void auditOp(OperationTypeEnum opType, boolean operationalStatus,
String operationStage, Object... descparams) {
URI tenantId;
URI username;
if (!BlockServiceUtils.hasValidUserInContext(_securityContext)
&& InterNodeHMACAuthFilter.isInternalRequest(_request)) {
// Use default values for internal datasvc requests that lack a user
// context
tenantId = _permissionsHelper.getRootTenant().getId();
username = ResourceService.INTERNAL_DATASVC_USER;
} else {
StorageOSUser user = BlockServiceUtils.getUserFromContext(_securityContext);
tenantId = URI.create(user.getTenantId());
username = URI.create(user.getName());
}
_auditLogManager.recordAuditLog(tenantId, username,
BlockService.EVENT_SERVICE_TYPE, opType, System.currentTimeMillis(),
operationalStatus ? AuditLogManager.AUDITLOG_SUCCESS
: AuditLogManager.AUDITLOG_FAILURE,
operationStage, descparams);
}
/**
* Cleans up after a failed request.
*
* @param taskList A list of prepared task responses.
* @param preparedObjects A collection of newly prepared ViPR database objects.
* @param errorMsg An error message.
* @param taskId The unique task identifier.
* @param sc A reference to a ServoceCoded.
*/
private <T extends DataObject> void cleanupFailure(List<TaskResourceRep> taskList, Collection<T> preparedObjects,
String errorMsg, String taskId, ServiceCoded sc) {
// Update the task responses to indicate an error occurred and also update
// the operation status map for the associated resource.
for (TaskResourceRep taskResourceRep : taskList) {
taskResourceRep.setState(Operation.Status.error.name());
taskResourceRep.setMessage(errorMsg);
_dbClient.error(BlockSnapshotSession.class, taskResourceRep.getResource().getId(), taskId, sc);
}
// Mark any newly prepared database objects inactive.
if (!preparedObjects.isEmpty()) {
for (DataObject object : preparedObjects) {
object.setInactive(true);
}
_dbClient.updateObject(preparedObjects);
}
}
/**
* 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;
}
Iterator<BlockConsistencyGroup> groups = _dbClient.queryIterativeObjects(BlockConsistencyGroup.class, consistencyGroups);
while (groups.hasNext()) {
BlockConsistencyGroup group = groups.next();
Operation op = _dbClient.createTaskOpStatus(BlockConsistencyGroup.class, group.getId(), taskId,
operationTypeEnum);
group.getOpStatus().put(taskId, op);
taskList.getTaskList().add(TaskMapper.toTask(group, taskId, op));
}
}
private ResourceOperationTypeEnum getCreateResourceOperationTypeEnum(BlockSnapshotSession session) {
return session.hasConsistencyGroup() ? ResourceOperationTypeEnum.CREATE_CONSISTENCY_GROUP_SNAPSHOT_SESSION
: ResourceOperationTypeEnum.CREATE_SNAPSHOT_SESSION;
}
private ResourceOperationTypeEnum getDeleteResourceOperationTypeEnum(BlockSnapshotSession session) {
return session.hasConsistencyGroup() ? ResourceOperationTypeEnum.DELETE_CONSISTENCY_GROUP_SNAPSHOT_SESSION
: ResourceOperationTypeEnum.DELETE_SNAPSHOT_SESSION;
}
private ResourceOperationTypeEnum getRelinkResourceOperationTypeEnum(BlockSnapshotSession session) {
return session.hasConsistencyGroup() ? ResourceOperationTypeEnum.RELINK_CONSISTENCY_GROUP_SNAPSHOT_SESSION_TARGETS
: ResourceOperationTypeEnum.RELINK_SNAPSHOT_SESSION_TARGETS;
}
private List<BlockObject> getAllSnapshotSessionSources(BlockSnapshotSession snapSession) {
if (snapSession.hasConsistencyGroup() && NullColumnValueGetter.isNotNullValue(snapSession.getReplicationGroupInstance())) {
BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, snapSession.getConsistencyGroup());
List<Volume> cgSources = BlockConsistencyGroupUtils.getAllCGVolumes(cg, _dbClient);
// return only those volumes belonging to session's RG
return ControllerUtils.getAllVolumesForRGInCG(cgSources,
snapSession.getReplicationGroupInstance(), snapSession.getStorageController(), _dbClient);
} else {
BlockObject snapSessionSourceObj = BlockSnapshotSessionUtils.querySnapshotSessionSource(snapSession.getParent().getURI(),
_uriInfo, true, _dbClient);
return Lists.newArrayList(snapSessionSourceObj);
}
}
}