/*
* Copyright 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.xtremio;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.customconfigcontroller.CustomConfigConstants;
import com.emc.storageos.customconfigcontroller.DataSource;
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.Project;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.client.util.NameGenerator;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.volumecontroller.SnapshotOperations;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator;
import com.emc.storageos.volumecontroller.impl.utils.ConsistencyGroupUtils;
import com.emc.storageos.volumecontroller.impl.xtremio.prov.utils.XtremIOProvUtils;
import com.emc.storageos.xtremio.restapi.XtremIOClient;
import com.emc.storageos.xtremio.restapi.XtremIOConstants;
import com.emc.storageos.xtremio.restapi.XtremIOConstants.XTREMIO_ENTITY_TYPE;
import com.emc.storageos.xtremio.restapi.model.response.XtremIOConsistencyGroup;
import com.emc.storageos.xtremio.restapi.model.response.XtremIOVolume;
public class XtremIOSnapshotOperations extends XtremIOOperations implements SnapshotOperations {
private static final Logger _log = LoggerFactory.getLogger(XtremIOSnapshotOperations.class);
private NameGenerator nameGenerator;
public void setNameGenerator(NameGenerator nameGenerator) {
this.nameGenerator = nameGenerator;
}
@Override
public void createSingleVolumeSnapshot(StorageSystem storage, URI snapshot, Boolean createInactive, Boolean readOnly,
TaskCompleter taskCompleter) throws DeviceControllerException {
BlockSnapshot snap = dbClient.queryObject(BlockSnapshot.class, snapshot);
try {
XtremIOClient client = XtremIOProvUtils.getXtremIOClient(dbClient, storage, xtremioRestClientFactory);
String xioClusterName = client.getClusterDetails(storage.getSerialNumber()).getName();
String generatedLabel = nameGenerator.generate("", snap.getLabel(), "",
'_', XtremIOConstants.XTREMIO_MAX_VOL_LENGTH);
snap.setLabel(generatedLabel);
XtremIOVolume createdSnap;
if (client.isVersion2()) {
createdSnap = createV2Snapshot(client, storage, xioClusterName, snap, generatedLabel, readOnly, taskCompleter);
} else {
createdSnap = createV1Snapshot(client, storage, snap, generatedLabel, readOnly, taskCompleter);
}
if (createdSnap != null) {
processSnapshot(createdSnap, snap, storage);
}
dbClient.updateObject(snap);
taskCompleter.ready(dbClient);
} catch (Exception e) {
_log.error("Snapshot creation failed", e);
snap.setInactive(true);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
}
private XtremIOVolume createV1Snapshot(XtremIOClient client, StorageSystem storage, BlockSnapshot snap, String snapLabel,
Boolean readOnly, TaskCompleter taskCompleter) throws Exception {
Volume parentVolume = dbClient.queryObject(Volume.class, snap.getParent().getURI());
URI projectUri = snap.getProject().getURI();
String clusterName = client.getClusterDetails(storage.getSerialNumber()).getName();
String snapFolderName = XtremIOProvUtils.createFoldersForVolumeAndSnaps(client, getVolumeFolderName(projectUri, storage))
.get(XtremIOConstants.SNAPSHOT_KEY);
String snapshotType = readOnly ? XtremIOConstants.XTREMIO_READ_ONLY_TYPE : XtremIOConstants.XTREMIO_REGULAR_TYPE;
client.createVolumeSnapshot(parentVolume.getDeviceLabel(), snapLabel, snapFolderName, snapshotType, clusterName);
XtremIOVolume createdSnap = client.getSnapShotDetails(snap.getLabel(), clusterName);
return createdSnap;
}
private XtremIOVolume createV2Snapshot(XtremIOClient client, StorageSystem storage, String xioClusterName, BlockSnapshot snap,
String snapLabel, Boolean readOnly, TaskCompleter taskCompleter) throws Exception {
Volume parentVolume = dbClient.queryObject(Volume.class, snap.getParent().getURI());
URI projectUri = snap.getProject().getURI();
String clusterName = client.getClusterDetails(storage.getSerialNumber()).getName();
String snapTagName = XtremIOProvUtils.createTagsForVolumeAndSnaps(client, getVolumeFolderName(projectUri, storage), clusterName)
.get(XtremIOConstants.SNAPSHOT_KEY);
String snapshotType = readOnly ? XtremIOConstants.XTREMIO_READ_ONLY_TYPE : XtremIOConstants.XTREMIO_REGULAR_TYPE;
client.createVolumeSnapshot(parentVolume.getDeviceLabel(), snapLabel, snapTagName, snapshotType, clusterName);
// Get the snapset details
XtremIOConsistencyGroup snapset = client.getSnapshotSetDetails(snapLabel, xioClusterName);
List<Object> snapDetails = snapset.getVolList().get(0);
XtremIOVolume xioSnap = client.getSnapShotDetails(snapDetails.get(1).toString(), xioClusterName);
// tag the created the snap
client.tagObject(snapTagName, XTREMIO_ENTITY_TYPE.SnapshotSet.name(), snapLabel, clusterName);
return xioSnap;
}
@Override
public void createGroupSnapshots(StorageSystem storage, List<URI> snapshotList, Boolean createInactive,
Boolean readOnly, TaskCompleter taskCompleter) throws DeviceControllerException {
try {
XtremIOClient client = XtremIOProvUtils.getXtremIOClient(dbClient, storage, xtremioRestClientFactory);
List<BlockSnapshot> snapObjs = dbClient.queryObject(BlockSnapshot.class, snapshotList);
BlockSnapshot snapshotObj = snapObjs.get(0);
BlockConsistencyGroup blockCG = dbClient.queryObject(BlockConsistencyGroup.class, snapshotObj.getConsistencyGroup());
// Check if the CG for which we are creating snapshot exists on the array
String clusterName = client.getClusterDetails(storage.getSerialNumber()).getName();
String snapsetLabel = snapshotObj.getSnapsetLabel();
String cgName = ConsistencyGroupUtils.getSourceConsistencyGroupName(snapshotObj, dbClient);
XtremIOConsistencyGroup cg = XtremIOProvUtils.isCGAvailableInArray(client, cgName, clusterName);
if (cg == null) {
_log.error("The consistency group does not exist in the array: {}", storage.getSerialNumber());
taskCompleter.error(dbClient, DeviceControllerException.exceptions
.consistencyGroupNotFound(cgName,
blockCG.getCgNameOnStorageSystem(storage.getId())));
return;
}
String snapType = readOnly ? XtremIOConstants.XTREMIO_READ_ONLY_TYPE : XtremIOConstants.XTREMIO_REGULAR_TYPE;
String snapshotSetTagName = XtremIOProvUtils.createTagsForVolumeAndSnaps(client,
getVolumeFolderName(snapshotObj.getProject().getURI(), storage), clusterName)
.get(XtremIOConstants.SNAPSHOT_KEY);
snapsetLabel = snapsetLabel + "_" + new SimpleDateFormat("yyyyMMddhhmmssSSS").format(new Date());
client.createConsistencyGroupSnapshot(cgName, snapsetLabel, "", snapType, clusterName);
// tag the created the snapshotSet
client.tagObject(snapshotSetTagName, XTREMIO_ENTITY_TYPE.SnapshotSet.name(), snapsetLabel, clusterName);
_log.info("Snapset label :{}", snapsetLabel);
// Create mapping of volume.deviceLabel to BlockSnapshot object
Map<String, BlockSnapshot> volumeToSnapMap = new HashMap<String, BlockSnapshot>();
for (BlockSnapshot snapshot : snapObjs) {
Volume volume = dbClient.queryObject(Volume.class, snapshot.getParent());
volumeToSnapMap.put(volume.getDeviceLabel(), snapshot);
}
// Get the snapset details
XtremIOConsistencyGroup snapset = client.getSnapshotSetDetails(snapsetLabel, clusterName);
for (List<Object> snapDetails : snapset.getVolList()) {
XtremIOVolume xioSnap = client.getSnapShotDetails(snapDetails.get(1).toString(), clusterName);
_log.info("XIO Snap : {}", xioSnap);
BlockSnapshot snapshot = volumeToSnapMap.get(xioSnap.getAncestoVolInfo().get(1));
if (snapshot != null) {
processSnapshot(xioSnap, snapshot, storage);
snapshot.setReplicationGroupInstance(snapsetLabel);
dbClient.updateObject(snapshot);
}
}
taskCompleter.ready(dbClient);
} catch (Exception e) {
_log.error("Snapshot creation failed", e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
}
private void processSnapshot(XtremIOVolume xioSnap, BlockSnapshot snapObj, StorageSystem storage) {
snapObj.setWWN(xioSnap.getVolInfo().get(0));
// if created snap wwn is not empty then update the wwn
if (!xioSnap.getWwn().isEmpty()) {
snapObj.setWWN(xioSnap.getWwn());
}
snapObj.setDeviceLabel(xioSnap.getVolInfo().get(1));
snapObj.setNativeId(xioSnap.getVolInfo().get(0));
String nativeGuid = NativeGUIDGenerator.generateNativeGuid(storage, snapObj);
snapObj.setNativeGuid(nativeGuid);
boolean isReadOnly = XtremIOConstants.XTREMIO_READ_ONLY_TYPE.equals(xioSnap.getSnapshotType()) ? Boolean.TRUE : Boolean.FALSE;
snapObj.setIsReadOnly(isReadOnly);
snapObj.setIsSyncActive(true);
snapObj.setProvisionedCapacity(Long.parseLong(xioSnap.getAllocatedCapacity()) * 1024);
snapObj.setAllocatedCapacity(Long.parseLong(xioSnap.getAllocatedCapacity()) * 1024);
}
@Override
public void activateSingleVolumeSnapshot(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void activateGroupSnapshots(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void deleteSingleVolumeSnapshot(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
try {
XtremIOClient client = XtremIOProvUtils.getXtremIOClient(dbClient, storage, xtremioRestClientFactory);
BlockSnapshot snapshotObj = dbClient.queryObject(BlockSnapshot.class, snapshot);
String clusterName = client.getClusterDetails(storage.getSerialNumber()).getName();
if (null != XtremIOProvUtils.isSnapAvailableInArray(client, snapshotObj.getDeviceLabel(), clusterName)) {
client.deleteSnapshot(snapshotObj.getDeviceLabel(), clusterName);
}
snapshotObj.setIsSyncActive(false);
snapshotObj.setInactive(true);
dbClient.updateObject(snapshotObj);
taskCompleter.ready(dbClient);
} catch (Exception e) {
_log.error("Snapshot deletion failed", e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
}
@Override
public void deleteGroupSnapshots(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException {
try {
XtremIOClient client = XtremIOProvUtils.getXtremIOClient(dbClient, storage, xtremioRestClientFactory);
BlockSnapshot snapshotObj = dbClient.queryObject(BlockSnapshot.class, snapshot);
String clusterName = client.getClusterDetails(storage.getSerialNumber()).getName();
String rgName = snapshotObj.getReplicationGroupInstance();
if (NullColumnValueGetter.isNotNullValue(rgName)
&& null != XtremIOProvUtils.isSnapsetAvailableInArray(client, rgName, clusterName)) {
client.deleteSnapshotSet(rgName, clusterName);
}
// Set inactive=true for all snapshots in the snap
List<BlockSnapshot> snapshots = ControllerUtils.getSnapshotsPartOfReplicationGroup(snapshotObj, dbClient);
for (BlockSnapshot snap : snapshots) {
snap.setIsSyncActive(false);
snap.setInactive(true);
dbClient.updateObject(snap);
}
taskCompleter.ready(dbClient);
} catch (Exception e) {
_log.error("Snapshot deletion failed", e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
}
@Override
public void restoreSingleVolumeSnapshot(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
try {
XtremIOClient client = XtremIOProvUtils.getXtremIOClient(dbClient, storage, xtremioRestClientFactory);
BlockSnapshot snapshotObj = dbClient.queryObject(BlockSnapshot.class, snapshot);
Volume sourceVol = dbClient.queryObject(Volume.class, snapshotObj.getParent());
String clusterName = client.getClusterDetails(storage.getSerialNumber()).getName();
client.restoreVolumeFromSnapshot(clusterName, sourceVol.getDeviceLabel(), snapshotObj.getDeviceLabel());
taskCompleter.ready(dbClient);
} catch (Exception e) {
_log.error("Snapshot restore failed", e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
}
@Override
public void restoreGroupSnapshots(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
try {
XtremIOClient client = XtremIOProvUtils.getXtremIOClient(dbClient, storage, xtremioRestClientFactory);
BlockSnapshot snapshotObj = dbClient.queryObject(BlockSnapshot.class, snapshot);
BlockConsistencyGroup group = dbClient.queryObject(BlockConsistencyGroup.class, snapshotObj.getConsistencyGroup());
// Check if the CG exists on the array
String clusterName = client.getClusterDetails(storage.getSerialNumber()).getName();
String cgName = ConsistencyGroupUtils.getSourceConsistencyGroupName(snapshotObj, dbClient);
XtremIOConsistencyGroup cg = XtremIOProvUtils.isCGAvailableInArray(client, cgName, clusterName);
if (cg == null) {
_log.error("The consistency group does not exist in the array: {}", storage.getSerialNumber());
taskCompleter.error(dbClient, DeviceControllerException.exceptions
.consistencyGroupNotFound(cgName, group.getCgNameOnStorageSystem(storage.getId())));
return;
}
client.restoreCGFromSnapshot(clusterName, cgName, snapshotObj.getReplicationGroupInstance());
taskCompleter.ready(dbClient);
} catch (Exception e) {
_log.error("Snapshot restore failed", e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
}
@Override
public void resyncSingleVolumeSnapshot(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
try {
XtremIOClient client = XtremIOProvUtils.getXtremIOClient(dbClient, storage, xtremioRestClientFactory);
BlockSnapshot snapshotObj = dbClient.queryObject(BlockSnapshot.class, snapshot);
Volume sourceVol = dbClient.queryObject(Volume.class, snapshotObj.getParent());
String clusterName = client.getClusterDetails(storage.getSerialNumber()).getName();
client.refreshSnapshotFromVolume(clusterName, sourceVol.getDeviceLabel(), snapshotObj.getDeviceLabel());
taskCompleter.ready(dbClient);
} catch (Exception e) {
_log.error("Snapshot resync failed", e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
}
@Override
public void resyncGroupSnapshots(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
try {
XtremIOClient client = XtremIOProvUtils.getXtremIOClient(dbClient, storage, xtremioRestClientFactory);
BlockSnapshot snapshotObj = dbClient.queryObject(BlockSnapshot.class, snapshot);
BlockConsistencyGroup group = dbClient.queryObject(BlockConsistencyGroup.class, snapshotObj.getConsistencyGroup());
// Check if the CG exists on the array
String clusterName = client.getClusterDetails(storage.getSerialNumber()).getName();
String cgName = ConsistencyGroupUtils.getSourceConsistencyGroupName(snapshotObj, dbClient);
XtremIOConsistencyGroup cg = XtremIOProvUtils.isCGAvailableInArray(client, cgName, clusterName);
if (cg == null) {
_log.error("The consistency group does not exist in the array: {}", storage.getSerialNumber());
taskCompleter.error(dbClient, DeviceControllerException.exceptions
.consistencyGroupNotFound(cgName,
group.getCgNameOnStorageSystem(storage.getId())));
return;
}
// providing noBackup option was resulting in an error which has been fixed in XIO 4.0.2 version.
// So for pre 4.0.2 version, do not use noBackup option.
if (XtremIOProvUtils.isXtremIOVersion402OrGreater(storage.getFirmwareVersion())) {
client.refreshSnapshotFromCG(clusterName, cgName, snapshotObj.getReplicationGroupInstance(), true);
} else {
client.refreshSnapshotFromCG(clusterName, cgName, snapshotObj.getReplicationGroupInstance(), false);
}
String newSnapsetName = null;
// Now get the new snapshot set name by querying back the snapshot
XtremIOVolume xioSnap = client.getSnapShotDetails(snapshotObj.getDeviceLabel(), clusterName);
if (xioSnap != null && xioSnap.getSnapSetList() != null && !xioSnap.getSnapSetList().isEmpty()) {
List<Object> snapsetDetails = xioSnap.getSnapSetList().get(0);
// The REST response for the snapsetList will contain 3 elements.
// Example - {"00a07269b55e42fa91c1aabadb6ea85c","SnapshotSet.1458111462198",27}
// We need the 2nd element which is the snapset name.
newSnapsetName = snapsetDetails.get(1).toString();
}
// Update the new snapshot set name in all the CG snapshots
if (NullColumnValueGetter.isNotNullValue(newSnapsetName)) {
List<BlockSnapshot> snapshots = ControllerUtils.getSnapshotsPartOfReplicationGroup(snapshotObj, dbClient);
for (BlockSnapshot snap : snapshots) {
_log.info("Updating replicationGroupInstance to {} in snapshot- {}:{}", newSnapsetName, snap.getLabel(), snap.getId());
snap.setReplicationGroupInstance(newSnapsetName);
dbClient.updateObject(snap);
}
}
taskCompleter.ready(dbClient);
} catch (Exception e) {
_log.error("Snapshot resync failed", e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
}
@Override
public void copySnapshotToTarget(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void copyGroupSnapshotsToTarget(StorageSystem storage, List<URI> snapshotList, TaskCompleter taskCompleter)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void terminateAnyRestoreSessions(StorageSystem storage, BlockObject from, URI volume, TaskCompleter taskCompleter)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
private String getVolumeFolderName(URI projectURI, StorageSystem storage) {
String volumeGroupFolderName = "";
Project project = dbClient.queryObject(Project.class, projectURI);
DataSource dataSource = dataSourceFactory.createXtremIOVolumeFolderNameDataSource(project, storage);
volumeGroupFolderName = customConfigHandler.getComputedCustomConfigValue(
CustomConfigConstants.XTREMIO_VOLUME_FOLDER_NAME, storage.getSystemType(), dataSource);
return volumeGroupFolderName;
}
@Override
public void establishVolumeSnapshotGroupRelation(StorageSystem storage, URI sourceVolume,
URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
/**
* {@inheritDoc}
*/
@Override
public void createSnapshotSession(StorageSystem system, URI snapSessionURI, TaskCompleter completer)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
/**
* {@inheritDoc}
*/
@Override
public void createGroupSnapshotSession(StorageSystem system, URI snapSessionURI, String groupName, TaskCompleter completer)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
/**
* {@inheritDoc}
*/
@Override
public void linkSnapshotSessionTarget(StorageSystem system, URI snapSessionURI, URI snapshotURI,
String copyMode, Boolean targetExists, TaskCompleter completer)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void linkSnapshotSessionTargetGroup(StorageSystem system, URI snapshotSessionURI, List<URI> snapSessionSnapshotURIs,
String copyMode, Boolean targetsExist, TaskCompleter completer) throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
/**
* {@inheritDoc}
*/
@Override
public void relinkSnapshotSessionTarget(StorageSystem system, URI tgtSnapSessionURI, URI snapshotURI,
TaskCompleter completer) throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
/**
* {@inheritDoc}
*/
@Override
public void relinkSnapshotSessionTargetGroup(StorageSystem system, URI tgtSnapSessionURI, URI snapshotURI,
TaskCompleter completer) throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
/**
* {@inheritDoc}
*/
@Override
public void unlinkSnapshotSessionTarget(StorageSystem system, URI snapSessionURI, URI snapshotURI,
Boolean deleteTarget, TaskCompleter completer) throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
/**
* {@inheritDoc}
*/
@Override
public void restoreSnapshotSession(StorageSystem system, URI snapSessionURI, TaskCompleter completer)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
/**
* {@inheritDoc}
*/
@Override
public void deleteSnapshotSession(StorageSystem system, URI snapSessionURI, String groupName, TaskCompleter completer)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
}