/*
* Copyright (c) 2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.smis;
import java.net.URI;
import java.util.List;
import javax.cim.CIMArgument;
import javax.cim.CIMInstance;
import javax.cim.CIMObjectPath;
import javax.cim.CIMProperty;
import com.emc.storageos.volumecontroller.impl.validators.ValidatorFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.BlockObject;
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.db.client.util.NameGenerator;
import com.emc.storageos.db.exceptions.DatabaseException;
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.SnapshotOperations;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl;
import com.emc.storageos.volumecontroller.impl.job.QueueJob;
import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockCreateSnapshotJob;
/**
* This class establishes a common, array-independent snapshot implementations.
*
*/
public abstract class AbstractSnapshotOperations implements SnapshotOperations {
private static final Logger _log = LoggerFactory.getLogger(AbstractSnapshotOperations.class);
protected DbClient _dbClient;
protected SmisCommandHelper _helper;
protected CIMObjectPathFactory _cimPath;
protected NameGenerator _nameGenerator;
protected ValidatorFactory validator;
public void setValidator(ValidatorFactory validator) {
this.validator = validator;
}
public void setCimObjectPathFactory(CIMObjectPathFactory cimObjectPathFactory) {
_cimPath = cimObjectPathFactory;
}
public void setDbClient(DbClient dbClient) {
_dbClient = dbClient;
}
public void setSmisCommandHelper(SmisCommandHelper smisCommandHelper) {
_helper = smisCommandHelper;
}
public void setNameGenerator(NameGenerator nameGenerator) {
_nameGenerator = nameGenerator;
}
/**
* 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 createInactive - Indicates if the snapshots should be created but not
* activated
* @param readOnly - Indicates if the snapshot should be read only.
* @param taskCompleter - TaskCompleter object used for the updating operation status.
*/
@Override
public void createSingleVolumeSnapshot(StorageSystem storage, URI snapshot, Boolean createInactive, Boolean readOnly,
TaskCompleter taskCompleter)
throws DeviceControllerException {
try {
BlockSnapshot snapshotObj = _dbClient.queryObject(BlockSnapshot.class, snapshot);
_log.info("createSingleVolumeSnapshot operation START");
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(), '-', SmisConstants.MAX_SNAPSHOT_NAME_LENGTH);
CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(storage);
CIMArgument[] inArgs = _helper.getCreateElementReplicaSnapInputArguments(storage, volume, createInactive, snapLabelToUse);
CIMArgument[] outArgs = new CIMArgument[5];
_helper.invokeMethod(storage, replicationSvcPath, SmisConstants.CREATE_ELEMENT_REPLICA, inArgs, outArgs);
CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB);
if (job != null) {
ControllerServiceImpl.enqueueJob(
new QueueJob(new SmisBlockCreateSnapshotJob(job,
storage.getId(), !createInactive, 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);
}
}
@Override
public void copySnapshotToTarget(StorageSystem storage, URI snapshot,
TaskCompleter taskCompleter)
throws DeviceControllerException {
// Default: no implementation because not every array needs to support this
// functionality
}
@Override
public void copyGroupSnapshotsToTarget(StorageSystem storage,
List<URI> snapshotList,
TaskCompleter taskCompleter)
throws DeviceControllerException {
// Default: no implementation because not every array needs to support this
// functionality
}
/**
* Method for deactivating a single snapshot instance. To be used as a common utility.
*
* @param storage [required] - StorageSystem object representing the array
* @param snapshot [required] - BlockSnapshot URI representing the previously created
* snap for the volume
* @param syncObjectPath [required] - The CIMObjectPath representing the block snapshot's
* SE_Synchronization object.
* @throws Exception
*/
protected void deactivateSnapshot(StorageSystem storage, BlockSnapshot snapshot, CIMObjectPath syncObjectPath)
throws Exception {
CIMInstance syncObject = _helper.getInstance(storage, syncObjectPath, false, false,
new String[] { SmisConstants.EMC_COPY_STATE_DESC });
String value = syncObject.getProperty(SmisConstants.EMC_COPY_STATE_DESC).getValue().toString();
_log.info(String.format("Attempting to deactivate snapshot %s, EMCCopyStateDesc = %s",
syncObjectPath.toString(), value));
if (value.equalsIgnoreCase(SmisConstants.ACTIVE)) {
CIMArgument[] outArgs = new CIMArgument[5];
_helper.callModifyReplica(storage, _helper.getDeactivateSnapshotSynchronousInputArguments(syncObjectPath), outArgs);
CIMProperty<CIMObjectPath> settingsData = (CIMProperty<CIMObjectPath>)
_cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.CP_SETTINGS_STATE).
getKey(SmisConstants.CP_SETTING_DATA);
String settingsInstance = settingsData.getValue().getKey(SmisConstants.CP_INSTANCE_ID).getValue().toString();
snapshot.setSettingsInstance(settingsInstance);
_dbClient.persistObject(snapshot);
}
}
/**
* Wrapper method will update the isSyncActive value of the snapshot object to the
* 'isActive' value.
*
* @param snapshot [required] - BlockSnapshot object to update
* @param isActive [required] - Value to set
*/
protected void setIsSyncActive(BlockSnapshot snapshot, boolean isActive) {
try {
snapshot.setIsSyncActive(isActive);
_dbClient.persistObject(snapshot);
} catch (DatabaseException e) {
_log.error(
String.format("Caught an IOException when trying to set refreshInProgress parameter for snapshot %s",
snapshot.getLabel()), e);
}
}
/**
* Wrapper method will update the isActive value of the snapshot object to the
* 'isActive' value.
*
* @param snapshots [required] - List of BlockSnapshot objects to update
* @param isActive [required] - Value to set
*/
protected void setIsSyncActive(List<BlockSnapshot> snapshots, boolean isActive) {
try {
for (BlockSnapshot snapshot : snapshots) {
snapshot.setIsSyncActive(isActive);
}
_dbClient.persistObject(snapshots);
} catch (DatabaseException e) {
_log.error("Caught an IOException when trying to set refreshInProgress snapshots", e);
}
}
/**
* Wrapper for setting the BlockSnapshot.inactive value
*
* @param snapshotURI [in] - BlockSnapshot object to update
* @param value [in] - Value to assign to inactive
*/
protected void setInactive(URI snapshotURI, boolean value) {
try {
if (snapshotURI != null) {
BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, snapshotURI);
snapshot.setInactive(value);
_dbClient.persistObject(snapshot);
}
} catch (DatabaseException e) {
_log.error("IOException when trying to update snapshot.inactive value", e);
}
}
/**
* Wrapper for setting the BlockSnapshot.inactive value
*
* @param snapshotURIs [in] - List of BlockSnapshot objects to update
* @param value [in] - Value to assign to inactive
*/
protected void setInactive(List<URI> snapshotURIs, boolean value) {
try {
if (snapshotURIs != null) {
for (URI uri : snapshotURIs) {
BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, uri);
snapshot.setInactive(value);
_dbClient.persistObject(snapshot);
}
}
} catch (DatabaseException e) {
_log.error("IOException when trying to update snapshot.inactive value", e);
}
}
@Override
public void terminateAnyRestoreSessions(StorageSystem storage, BlockObject from, URI volume,
TaskCompleter taskCompleter) throws Exception {
// Default: no implementation because not every array needs to support this
// functionality
}
@Override
public void establishVolumeSnapshotGroupRelation(StorageSystem storage, URI sourceVolume,
URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void resyncSingleVolumeSnapshot(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter) {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void resyncGroupSnapshots(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter) {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
}