/*
* Copyright (c) 2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.block.taskcompleter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.Operation;
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.exceptions.DeviceControllerException;
import com.emc.storageos.security.audit.AuditLogManager;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.svcs.errorhandling.model.ServiceCoded;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.volumecontroller.impl.monitoring.RecordableBourneEvent;
import com.emc.storageos.volumecontroller.impl.monitoring.RecordableEventManager;
import com.emc.storageos.volumecontroller.impl.monitoring.cim.enums.RecordType;
import com.google.common.collect.Lists;
/**
* Abstract base task completer for operations on BlockSnapshotSession instances.
*/
@SuppressWarnings("serial")
public abstract class BlockSnapshotSessionCompleter extends TaskCompleter {
// A logger.
private static final Logger s_logger = LoggerFactory.getLogger(BlockSnapshotSessionCompleter.class);
protected List<URI> _snapshotURIs;
/**
* Constructor
*
* @param snapSessionURIs The URIs of the BlockSnapshotSession instances.
* @param taskId The unique task identifier.
*/
public BlockSnapshotSessionCompleter(List<URI> snapSessionURIs, String taskId) {
super(BlockSnapshotSession.class, snapSessionURIs, taskId);
}
/**
* Constructor
*
* @param snapSessionURI The URI of the BlockSnapshotSession instance.
* @param taskId The unique task identifier.
*/
public BlockSnapshotSessionCompleter(URI snapSessionURI, String taskId) {
super(BlockSnapshotSession.class, snapSessionURI, taskId);
}
@Override
protected void complete(DbClient dbClient, Operation.Status status, ServiceCoded coded) throws DeviceControllerException {
updateConsistencyGroupTasks(dbClient, status, coded);
if (isNotifyWorkflow()) {
updateWorkflowStatus(status, coded);
}
}
/**
*
* Records a ViPR event and creates an audit log entry to capture the results of the
* BlockSnapshotSession operation.
*
* @param dbClient A reference to a database client.
* @param opType The snapshot session operation type.
* @param status The result of the request.
* @param snapSession A reference to the BlockSnapshotSession instance.
* @param sourceObj A reference to the source object.
*/
protected void recordBlockSnapshotSessionOperation(DbClient dbClient, OperationTypeEnum opType,
Operation.Status status, BlockSnapshotSession snapSession, BlockObject sourceObj) {
try {
boolean opStatus = (Operation.Status.ready == status) ? true : false;
String eventType = opType.getEvType(opStatus);
String description = getDescriptionOfResults(status, sourceObj, snapSession);
s_logger.info("opType: {} detail: {}", opType.toString(), eventType + ':' + description);
String snapSessionId = snapSession.getId().toString();
String snapSessionLabel = snapSession.getLabel();
String sourceObjId = sourceObj.getId().toString();
String opStage = AuditLogManager.AUDITOP_END;
// Record the ViPR event.
recordBlockSnapshotSessionEvent(dbClient, snapSession, eventType, status, description);
switch (opType) {
case CREATE_SNAPSHOT_SESSION:
if (opStatus) {
AuditBlockUtil.auditBlock(dbClient, opType, opStatus, opStage, snapSessionId, snapSessionLabel, sourceObjId);
} else {
AuditBlockUtil.auditBlock(dbClient, opType, opStatus, opStage, snapSessionLabel, sourceObjId);
}
break;
case RESTORE_SNAPSHOT_SESSION:
case DELETE_SNAPSHOT_SESSION:
case LINK_SNAPSHOT_SESSION_TARGET:
case UNLINK_SNAPSHOT_SESSION_TARGET:
case RELINK_SNAPSHOT_SESSION_TARGET:
case DELETE_VOLUME_SNAPSHOT:
case DELETE_CONSISTENCY_GROUP_SNAPSHOT:
AuditBlockUtil.auditBlock(dbClient, opType, opStatus, opStage, snapSessionId, snapSessionLabel, sourceObjId);
break;
default:
s_logger.error("Unrecognized block snapshot sesion operation type");
}
} catch (Exception e) {
s_logger.error("Failed to record block snapshot session operation {}, err: ", opType.toString(), e);
}
}
/**
* Records a ViPR event for a the BlockSnapshotSession operation.
*
* @param dbClient A reference to a database client.
* @param snapSession A reference to the snap shot session.
* @param evtType The event type.
* @param status The results of the request.
* @param description A description of the results.
*/
protected void recordBlockSnapshotSessionEvent(DbClient dbClient, BlockSnapshotSession snapSession, String evtType,
Operation.Status status, String description) {
RecordableEventManager eventManager = new RecordableEventManager();
eventManager.setDbClient(dbClient);
RecordableBourneEvent event = ControllerUtils.convertToRecordableBourneEvent(snapSession, evtType, description, "", dbClient,
ControllerUtils.BLOCK_EVENT_SERVICE, RecordType.Event.name(), ControllerUtils.BLOCK_EVENT_SOURCE);
try {
eventManager.recordEvents(event);
s_logger.info("Bourne {} event recorded", evtType);
} catch (Exception ex) {
s_logger.error("Failed to record event. Event description: {}. Error: ", evtType, ex);
}
}
/**
* Gets a description of the operation results.
*
* @param status The results of the request.
* @param sourceObj The source object for the snapshot session
* @param snapSession The snapshot session
*
* @return The operation description.
*/
protected String getDescriptionOfResults(Operation.Status status, BlockObject sourceObj, BlockSnapshotSession snapSession) {
return null;
}
/**
* Returns all appropriate sources for a given BlockSnapshotSession. That is, VPLEX volumes if
* they exist or native backend volumes.
*
* For volumes that are not in any consistency group, the returned list shall contain only one element.
*
* @param snapSession BlockSnapshotSession.
* @param dbClient Database client.
* @return List of one or more BlockObject instances.
*/
protected List<BlockObject> getAllSources(BlockSnapshotSession snapSession, DbClient dbClient) {
if (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);
}
return Lists.newArrayList(getSource(snapSession, dbClient));
}
/**
* Returns the appropriate source for a BlockSnapshotSession. That is, a VPLEX volume if
* one exists or a native backend volume.
*
* @param snapshotSession BlockSnapshotSession with a valid parent (no consistency group).
* @param dbClient Database client.
* @return BlockObject representing the snapshot session source.
*/
protected BlockObject getSource(BlockSnapshotSession snapshotSession, DbClient dbClient) {
URI parentURI = snapshotSession.getParent().getURI();
if (URIUtil.isNull(parentURI)) {
throw new IllegalArgumentException("Expected a BlockSnapshotSession with a non-null parent");
}
BlockObject object = BlockObject.fetch(dbClient, parentURI);
if (Volume.checkForVplexBackEndVolume(dbClient, (Volume) object)) {
return Volume.fetchVplexVolume(dbClient, (Volume) object);
}
return object;
}
/**
* For non-CG snapshots, the returned list contains only the passed in snapshot.
* For CG snapshots, the returned list contains all snapshot members of the passed in snapshot's
* replication group.
*
* @param snapshot
* @param dbClient
* @return
*/
public List<BlockSnapshot> getRelatedSnapshots(BlockSnapshot snapshot, DbClient dbClient) {
List<BlockSnapshot> result = new ArrayList<>();
if (snapshot.hasConsistencyGroup()) {
result.addAll(ControllerUtils.getSnapshotsPartOfReplicationGroup(
snapshot, dbClient));
} else {
result.add(snapshot);
}
return result;
}
/**
* When this completer is handling multiple snapshots from different replication groups,
* this method gathers all related snapshots for each snapshot and returns them in a list.
*
* @param dbClient Database client.
* @return List of all snapshots, including each of their related snapshots.
*/
public List<BlockSnapshot> getAllSnapshots(DbClient dbClient) {
List<BlockSnapshot> result = new ArrayList<>();
Iterator<BlockSnapshot> iterator = dbClient.queryIterativeObjects(BlockSnapshot.class, _snapshotURIs);
while (iterator.hasNext()) {
BlockSnapshot snapshot = iterator.next();
result.addAll(getRelatedSnapshots(snapshot, dbClient));
}
return result;
}
}