/*
* Copyright (c) 2008-2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.smis.ibm.xiv;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import javax.cim.CIMArgument;
import javax.cim.CIMObjectPath;
import javax.wbem.WBEMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.model.BlockSnapshot;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.TenantOrg;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.exceptions.DeviceControllerErrors;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.volumecontroller.impl.block.taskcompleter.BlockSnapshotCreateCompleter;
import com.emc.storageos.volumecontroller.impl.smis.AbstractSnapshotOperations;
import com.emc.storageos.volumecontroller.impl.smis.ibm.IBMCIMObjectPathFactory;
import com.emc.storageos.volumecontroller.impl.smis.ibm.IBMSmisConstants;
public class XIVSnapshotOperations extends AbstractSnapshotOperations {
private static final Logger _log = LoggerFactory
.getLogger(XIVSnapshotOperations.class);
private XIVSmisCommandHelper _helper;
private IBMCIMObjectPathFactory _cimPath;
private XIVSmisStorageDevicePostProcessor _smisStorageDevicePostProcessor;
public void setCimObjectPathFactory(
IBMCIMObjectPathFactory cimObjectPathFactory) {
_cimPath = cimObjectPathFactory;
}
// to suppress Spring ambiguous write method warning
public IBMCIMObjectPathFactory getCimObjectPathFactory() {
return _cimPath;
}
public void setSmisCommandHelper(XIVSmisCommandHelper smisCommandHelper) {
_helper = smisCommandHelper;
}
// to suppress Spring ambiguous write method warning
public XIVSmisCommandHelper getSmisCommandHelper() {
return _helper;
}
public void setSmisStorageDevicePostProcessor(
XIVSmisStorageDevicePostProcessor smisStorageDevicePostProcessor) {
_smisStorageDevicePostProcessor = smisStorageDevicePostProcessor;
}
/**
* Should implement creation of a single volume snapshot. That is a volume
* that is not in any consistency group.
*
* @param storage
* [required] - StorageSystem object representing the array
* @param snapshot
* [required] - BlockSnapshot URI representing the previously
* created snap for the volume
* @param taskCompleter
* - TaskCompleter object used for the updating operation status.
*/
@SuppressWarnings("rawtypes")
@Override
public void createSingleVolumeSnapshot(StorageSystem storage, URI snapshot,
Boolean createInactive, Boolean readOnly, TaskCompleter taskCompleter)
throws DeviceControllerException {
_log.info("createSingleVolumeSnapshot operation START");
try {
BlockSnapshot snapshotObj = _dbClient.queryObject(
BlockSnapshot.class, snapshot);
Volume volume = _dbClient.queryObject(Volume.class,
snapshotObj.getParent());
TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, volume
.getTenant().getURI());
String tenantName = tenant.getLabel();
String snapLabelToUse = _nameGenerator.generate(tenantName,
snapshotObj.getLabel(), snapshot.toString(), '-',
IBMSmisConstants.MAX_SNAPSHOT_NAME_LENGTH);
CIMArgument[] inArgs = _helper
.getCreateElementReplicaSnapInputArguments(storage, volume,
createInactive, snapLabelToUse);
CIMArgument[] outArgs = new CIMArgument[5];
_helper.callReplicationSvc(storage,
IBMSmisConstants.CREATE_ELEMENT_REPLICA, inArgs, outArgs);
_smisStorageDevicePostProcessor.processSnapshotCreation(storage,
snapshot, !createInactive, outArgs,
(BlockSnapshotCreateCompleter) taskCompleter);
} catch (Exception e) {
_log.info("Problem making SMI-S call: ", e);
ServiceError error = DeviceControllerErrors.smis
.unableToCallStorageProvider(e.getMessage());
taskCompleter.error(_dbClient, error);
setInactive(snapshot, true);
}
}
/**
* Implementation for restoring of a single volume snapshot restore. That
* is, this volume is independent of other volumes and a snapshot was taken
* previously, and now we want to restore that snap to the original volume.
*
* @param storage
* [required] - StorageSystem object representing the array
* @param volume
* [required] - Volume URI for the volume to be restored
* @param snapshot
* [required] - BlockSnapshot URI representing the previously
* created snap for the volume
* @param taskCompleter
* - TaskCompleter object used for the updating operation status.
*/
@Override
public void restoreSingleVolumeSnapshot(StorageSystem storage, URI volume,
URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
try {
BlockSnapshot from = _dbClient.queryObject(BlockSnapshot.class,
snapshot);
Volume to = _dbClient.queryObject(Volume.class, volume);
CIMObjectPath syncObjectPath = _cimPath
.getSyncObject(storage, from);
if (_helper.checkExists(storage, syncObjectPath, false, false) != null) {
_log.info(
"Volume {} is not thinly provisioned, will attempt restore",
to.getLabel());
_helper.callModifyReplica(storage, _helper
.getRestoreFromSnapshotInputArguments(syncObjectPath));
taskCompleter.ready(_dbClient);
} else {
ServiceError error = DeviceControllerErrors.smis
.unableToFindSynchPath(storage.getLabel());
taskCompleter.error(_dbClient, error);
}
} catch (WBEMException e) {
String message = String
.format("Error encountered when trying to restore from snapshot %s on array %s",
snapshot.toString(), storage.getLabel());
_log.error(message, e);
ServiceError error = DeviceControllerErrors.smis
.unableToCallStorageProvider(e.getMessage());
taskCompleter.error(_dbClient, error);
} catch (Exception e) {
String message = String
.format("Generic exception when trying to restore from snapshot %s on array %s",
snapshot.toString(), storage.getLabel());
_log.error(message, e);
ServiceError error = DeviceControllerErrors.smis.methodFailed(
"restoreSingleVolumeSnapshot", e.getMessage());
taskCompleter.error(_dbClient, error);
}
}
/**
* Should implement deletion of single volume snapshot. That is, deleting a
* snap that was created independent of other volumes.
*
* @param storage
* [required] - StorageSystem object representing the array
* @param snapshot
* [required] - BlockSnapshot URI representing the previously
* created snap for the volume
* @param taskCompleter
* - TaskCompleter object used for the updating operation status.
*/
@Override
public void deleteSingleVolumeSnapshot(StorageSystem storage, URI snapshot,
TaskCompleter taskCompleter) throws DeviceControllerException {
try {
BlockSnapshot snap = _dbClient.queryObject(BlockSnapshot.class,
snapshot);
CIMObjectPath syncObjectPath = _cimPath
.getSyncObject(storage, snap);
if (_helper.checkExists(storage, syncObjectPath, false, false) != null) {
_helper.callModifyReplica(
storage,
_helper.getDeleteSnapshotSynchronousInputArguments(syncObjectPath));
}
// set regardless sync object path exists or not
// Perhaps, it's already been deleted or was deleted on the array.
// In that case, we'll just say all is well, so that this operation
// is idempotent.
snap.setInactive(true);
snap.setIsSyncActive(false);
_dbClient.updateObject(snap);
taskCompleter.ready(_dbClient);
} catch (WBEMException e) {
String message = String.format(
"Error encountered during delete snapshot %s on array %s",
snapshot.toString(), storage.getSerialNumber());
_log.error(message, e);
ServiceError error = DeviceControllerErrors.smis
.unableToCallStorageProvider(e.getMessage());
taskCompleter.error(_dbClient, error);
} catch (Exception e) {
String message = String
.format("Generic exception when trying to delete snapshot %s on array %s",
snapshot.toString(), storage.getSerialNumber());
_log.error(message, e);
ServiceError error = DeviceControllerErrors.smis.methodFailed(
"deleteSingleVolumeSnapshot", e.getMessage());
taskCompleter.error(_dbClient, error);
}
}
/**
* Should implement create of a snapshot from a source volume that is part
* of a consistency group.
*
* @param storage
* [required] - StorageSystem object representing the array
* @param taskCompleter
* - TaskCompleter object used for the updating operation status.
* @param snapshot
* [required] - BlockSnapshot URI representing the previously
* created snap for the volume
* @throws DeviceControllerException
*/
@SuppressWarnings("rawtypes")
@Override
public void createGroupSnapshots(StorageSystem storage,
List<URI> snapshotList, Boolean createInactive,
Boolean readOnly, TaskCompleter taskCompleter) throws DeviceControllerException {
try {
URI snapshot = snapshotList.get(0);
BlockSnapshot snapshotObj = _dbClient.queryObject(
BlockSnapshot.class, snapshot);
Volume volume = _dbClient.queryObject(Volume.class,
snapshotObj.getParent());
TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, volume
.getTenant().getURI());
String tenantName = tenant.getLabel();
String snapLabelToUse = _nameGenerator.generate(tenantName,
snapshotObj.getSnapsetLabel(), snapshot.toString(), '-',
IBMSmisConstants.MAX_SNAPSHOT_NAME_LENGTH);
// check if the snapshot group name is used on array, return if the name is used to avoid unnecessary CIM call
// snapshot group name has to be unique on array
CIMObjectPath sgPath = _cimPath.getSnapshotGroupPath(storage, snapLabelToUse);
if (sgPath != null) {
_log.error("Failed to create group snapshots: " + IBMSmisConstants.DUPLICATED_SG_NAME_ERROR);
ServiceError error = DeviceControllerErrors.smis.methodFailed(
"createGroupSnapshots", IBMSmisConstants.DUPLICATED_SG_NAME_ERROR);
taskCompleter.error(_dbClient, error);
setInactive(snapshotList, true);
return;
}
String groupName = _helper.getConsistencyGroupName(snapshotObj, storage);
CIMObjectPath cgPath = _cimPath.getConsistencyGroupPath(storage,
groupName);
CIMArgument[] inArgs = _helper.getCreateGroupReplicaInputArguments(
storage, cgPath, createInactive, snapLabelToUse);
CIMArgument[] outArgs = new CIMArgument[5];
_helper.callReplicationSvc(storage,
IBMSmisConstants.CREATE_GROUP_REPLICA, inArgs, outArgs);
_smisStorageDevicePostProcessor.processCGSnapshotCreation(storage,
snapshotList, !createInactive, snapLabelToUse,
(BlockSnapshotCreateCompleter) taskCompleter);
} catch (Exception e) {
_log.info("Problem making SMI-S call: ", e);
ServiceError error = DeviceControllerErrors.smis
.unableToCallStorageProvider(e.getMessage());
taskCompleter.error(_dbClient, error);
setInactive(snapshotList, true);
}
}
/**
* Implementation should restore the set of snapshots that were taken for a
* set of volumes in a consistency group. That is, at some time there was a
* consistency group of volumes created and snapshot was taken of these;
* these snapshots would belong to a "snap-set". This restore operation,
* will restore the volumes in the consistency group from this snap-set. Any
* snapshot from the snap-set can be provided to restore the whole snap-set.
*
* @param storage
* [required] - StorageSystem object representing the array
* @param snapshot
* [required] - BlockSnapshot URI representing the previously
* created snap for the volume
* @param taskCompleter
* - TaskCompleter object used for the updating operation status.
*/
@Override
public void restoreGroupSnapshots(StorageSystem storage, URI volume,
URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
try {
BlockSnapshot snapshotObj = _dbClient.queryObject(
BlockSnapshot.class, snapshot);
String snapshotGroupName = snapshotObj.getReplicationGroupInstance();
CIMObjectPath groupSynchronized = _cimPath
.getGroupSynchronizedPath(storage, snapshotGroupName);
if (groupSynchronized != null) {
// restore snapshot
_helper.callModifyReplica(
storage,
_helper.getRestoreFromSnapshotInputArguments(groupSynchronized));
taskCompleter.ready(_dbClient);
} else {
_log.error("No GroupSynchronized object found for " + snapshotGroupName);
ServiceError error = DeviceControllerErrors.smis.methodFailed(
"restoreGroupSnapshots", "No GroupSynchronized object found");
taskCompleter.error(_dbClient, error);
}
} catch (Exception e) {
String message = String
.format("Generic exception when trying to restoring snapshots from consistency group on array %s",
storage.getSerialNumber());
_log.error(message, e);
ServiceError error = DeviceControllerErrors.smis.methodFailed(
"restoreGroupSnapshots", e.getMessage());
taskCompleter.error(_dbClient, error);
}
}
/**
* Should implement clean up of all the snapshots in a volume consistency
* group 'snap-set'. The 'snap-set' is a set of block snapshots created for
* a set of volumes in a consistency group.
*
* @param storage
* [required] - StorageSystem object representing the array
* @param snapshot
* [required] - BlockSnapshot object representing the previously
* created snap for the volume
* @param taskCompleter
* - TaskCompleter object used for the updating operation status.
*/
@Override
public void deleteGroupSnapshots(StorageSystem storage, URI snapshot,
TaskCompleter taskCompleter) throws DeviceControllerException {
try {
_log.info("deleteGroupSnapshots operation {}", snapshot);
List<BlockSnapshot> snapshots = _dbClient.queryObject(
BlockSnapshot.class, Arrays.asList(snapshot));
BlockSnapshot snapshotObj = snapshots.get(0);
CIMObjectPath groupSynchronized = _cimPath.getGroupSynchronizedPath(storage, snapshotObj.getReplicationGroupInstance());
if (groupSynchronized != null) {
// Delete the snapshot group (not the consistency group). This
// group is for
// the specific snaps that were taken for the consistency group.
try {
_helper.callModifyReplica(storage,
_helper.getReturnGroupSyncToPoolInputArguments(groupSynchronized));
} catch (Exception e) {
// If the snapshot group doesn't exist, ignore the exception.
// This is necessary in case of removing multiple snapshots from UI.
// E.g., snapshots v1_s1 and v2_s1 are part of snapshot group 1,
// v1_s2 and v2_s2 are part of snapshot group 2.
// When removing the four snapshots from UI in one request, it will create four tasks for deleting
// the snapshots from the two snapshot groups. Essentially, two tasks will race on the deleting.
// SMI-S call will return code 36944 if the groupSynchronized instance doesn't exist anymore.
CIMObjectPath snapGroup = (CIMObjectPath) groupSynchronized.getKeyValue(IBMSmisConstants.CP_SYNCED_ELEMENT);
if (_helper.checkExists(storage, snapGroup, false, false) != null) {
throw e;
}
}
}
// set inactive=true for all snapshots in the snaps set
List<BlockSnapshot> snaps = ControllerUtils.getSnapshotsPartOfReplicationGroup(snapshotObj, _dbClient);
for (BlockSnapshot snap : snaps) {
snap.setInactive(true);
snap.setIsSyncActive(false);
_dbClient.updateObject(snap);
}
taskCompleter.ready(_dbClient);
} catch (Exception e) {
String message = String
.format("Generic exception when trying to delete snapshots from consistency group on array %s",
storage.getSerialNumber());
_log.error(message, e);
ServiceError error = DeviceControllerErrors.smis.methodFailed(
"deleteGorupSnapshots", e.getMessage());
taskCompleter.error(_dbClient, error);
}
}
@Override
public void activateSingleVolumeSnapshot(StorageSystem storage,
URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
// not supported by IBM XIV
}
@Override
public void activateGroupSnapshots(StorageSystem storage, URI snapshot,
TaskCompleter taskCompleter) throws DeviceControllerException {
// not supported by IBM XIV
}
/**
* {@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();
}
}