/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.cinder;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.cinder.CinderConstants;
import com.emc.storageos.cinder.CinderEndPointInfo;
import com.emc.storageos.cinder.api.CinderApi;
import com.emc.storageos.cinder.api.CinderApiFactory;
import com.emc.storageos.cinder.errorhandling.CinderException;
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.ExportMask;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.StoragePool;
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.svcs.errorhandling.resources.InternalException;
import com.emc.storageos.volumecontroller.CloneOperations;
import com.emc.storageos.volumecontroller.DefaultBlockStorageDevice;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.volumecontroller.impl.VolumeURIHLU;
import com.emc.storageos.volumecontroller.impl.block.taskcompleter.MultiVolumeTaskCompleter;
import com.emc.storageos.volumecontroller.impl.block.taskcompleter.VolumeTaskCompleter;
import com.emc.storageos.volumecontroller.impl.cinder.job.CinderDeleteVolumeJob;
import com.emc.storageos.volumecontroller.impl.cinder.job.CinderJob;
import com.emc.storageos.volumecontroller.impl.cinder.job.CinderMultiVolumeCreateJob;
import com.emc.storageos.volumecontroller.impl.cinder.job.CinderSingleVolumeCreateJob;
import com.emc.storageos.volumecontroller.impl.cinder.job.CinderSnapshotCreateJob;
import com.emc.storageos.volumecontroller.impl.cinder.job.CinderSnapshotDeleteJob;
import com.emc.storageos.volumecontroller.impl.cinder.job.CinderVolumeExpandJob;
import com.emc.storageos.volumecontroller.impl.job.QueueJob;
import com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations;
import com.emc.storageos.volumecontroller.impl.smis.SmisConstants;
import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper;
/**
* OpenStack Cinder specific provisioning implementation class.
* This class is responsible to do all provisioning operations by interacting with Cinder REST API
*
*/
public class CinderStorageDevice extends DefaultBlockStorageDevice {
private static final Logger log = LoggerFactory.getLogger(CinderStorageDevice.class);
private DbClient dbClient;
private CinderApiFactory cinderApiFactory;
private ExportMaskOperations _exportMaskOperationsHelper;
private CloneOperations cloneOperations;
private NameGenerator nameGenerator;
private final long SNAPSHOT_DELETE_STATUS_CHECK_SLEEP_TIME = 10 * 1000; // 10 seconds
public void setDbClient(DbClient dbClient) {
this.dbClient = dbClient;
}
/**
* @param cinderApiFactory the CinderApiFactory to set
*/
public void setCinderApiFactory(CinderApiFactory cinderApiFactory) {
this.cinderApiFactory = cinderApiFactory;
}
public void setExportMaskOperationsHelper(ExportMaskOperations exportMaskOperationsHelper) {
_exportMaskOperationsHelper = exportMaskOperationsHelper;
}
public void setCloneOperations(final CloneOperations cloneOperations) {
this.cloneOperations = cloneOperations;
}
public void setNameGenerator(final NameGenerator nameGenerator) {
this.nameGenerator = nameGenerator;
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doCreateVolumes
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.StoragePool,
* java.lang.String, java.util.List,
* com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doCreateVolumes(StorageSystem storageSystem, StoragePool storagePool,
String opId, List<Volume> volumes,
VirtualPoolCapabilityValuesWrapper capabilities,
TaskCompleter taskCompleter) throws DeviceControllerException {
String label = null;
Long capacity = null;
boolean opCreationFailed = false;
StringBuilder logMsgBuilder = new StringBuilder(String.format(
"Create Volume Start - Array:%s, Pool:%s",
storageSystem.getSerialNumber(),
storagePool.getNativeGuid()));
for (Volume volume : volumes) {
logMsgBuilder
.append(String.format("%nVolume:%s", volume.getLabel()));
String tenantName = "";
try {
TenantOrg tenant = dbClient.queryObject(TenantOrg.class, volume.getTenant().getURI());
tenantName = tenant.getLabel();
} catch (DatabaseException e) {
log.error("Error lookup TenantOrg object", e);
}
label = nameGenerator.generate(tenantName, volume.getLabel(),
volume.getId().toString(), CinderConstants.CHAR_HYPHEN,
SmisConstants.MAX_VOLUME_NAME_LENGTH);
if (capacity == null) {
capacity = volume.getCapacity();
}
}
log.info(logMsgBuilder.toString());
try {
CinderEndPointInfo ep = CinderUtils.getCinderEndPoint(storageSystem.getActiveProviderURI(), dbClient);
log.info("Getting the cinder APi for the provider with id {}", storageSystem.getActiveProviderURI());
CinderApi cinderApi = cinderApiFactory.getApi(storageSystem.getActiveProviderURI(), ep);
String volumeId = null;
Map<String, URI> volumeIds = new HashMap<String, URI>();
if (volumes.size() == 1) {
volumeId = cinderApi.createVolume(label, CinderUtils.convertToGB(capacity), storagePool.getNativeId());
volumeIds.put(volumeId, volumes.get(0).getId());
log.debug("Creating volume with the id {} on Openstack cinder node", volumeId);
} else {
log.debug("Starting to create {} volumes", volumes.size());
for (int volumeIndex = 0; volumeIndex < volumes.size(); volumeIndex++) {
volumeId = cinderApi.createVolume(label + CinderConstants.HYPHEN + (volumeIndex + 1),
CinderUtils.convertToGB(capacity), storagePool.getNativeId());
volumeIds.put(volumeId, volumes.get(volumeIndex).getId());
log.debug("Creating volume with the id {} on Openstack cinder node", volumeId);
}
}
if (!volumeIds.isEmpty()) {
CinderJob createVolumeJob = (volumes.size() > 1)
? new CinderMultiVolumeCreateJob(volumeId, label, volumes.get(0).getStorageController(),
CinderConstants.ComponentType.volume.name(), ep, taskCompleter, storagePool.getId(), volumeIds)
: new CinderSingleVolumeCreateJob(volumeId, label, volumes.get(0).getStorageController(),
CinderConstants.ComponentType.volume.name(), ep, taskCompleter, storagePool.getId(), volumeIds);
ControllerServiceImpl.enqueueJob(new QueueJob(createVolumeJob));
}
} catch (final InternalException e) {
log.error("Problem in doCreateVolumes: ", e);
opCreationFailed = true;
taskCompleter.error(dbClient, e);
} catch (final Exception e) {
log.error("Problem in doCreateVolumes: ", e);
opCreationFailed = true;
ServiceError serviceError = DeviceControllerErrors.cinder.operationFailed("doCreateVolumes", e.getMessage());
taskCompleter.error(dbClient, serviceError);
}
if (opCreationFailed) {
for (Volume vol : volumes) {
vol.setInactive(true);
dbClient.persistObject(vol);
}
}
logMsgBuilder = new StringBuilder(String.format(
"Create Volumes End - Array:%s, Pool:%s",
storageSystem.getSerialNumber(),
storagePool.getNativeGuid()));
for (Volume volume : volumes) {
logMsgBuilder.append(String.format("%nVolume:%s", volume.getLabel()));
}
log.info(logMsgBuilder.toString());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExpandVolume
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.StoragePool,
* com.emc.storageos.db.client.model.Volume,
* java.lang.Long,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExpandVolume(StorageSystem storageSystem, StoragePool storagePool,
Volume volume, Long size, TaskCompleter taskCompleter)
throws DeviceControllerException {
try {
log.info(String.format("Expand Volume Start - Array:%s, Pool:%s, Volume:%s",
storageSystem.getSerialNumber(), storagePool.getNativeGuid(), volume.getId()));
CinderEndPointInfo ep = CinderUtils.getCinderEndPoint(storageSystem.getActiveProviderURI(), dbClient);
log.info("Getting the cinder APi for the provider with id " + storageSystem.getActiveProviderURI());
CinderApi cinderApi = cinderApiFactory.getApi(storageSystem.getActiveProviderURI(), ep);
String volumeId = volume.getNativeId();
if (null != volumeId) {
log.info("Expanding volume with the id " + volumeId + " on Openstack cinder node");
cinderApi.expandVolume(volumeId, CinderUtils.convertToGB(size));
CinderJob expandVolumeJob = new CinderVolumeExpandJob(volumeId, volume.getLabel(),
volume.getStorageController(), CinderConstants.ComponentType.volume.name(),
ep, taskCompleter, storagePool.getId());
ControllerServiceImpl.enqueueJob(new QueueJob(expandVolumeJob));
}
log.info(String.format("Expand Volume End - Array:%s, Pool:%s, Volume:%s",
storageSystem.getSerialNumber(), storagePool.getNativeGuid(), volume.getId()));
} catch (CinderException ce) {
String message = String.format(
"Exception when trying to expand volume on Array %s, Pool:%s, Volume:%s",
storageSystem.getSerialNumber(), storagePool.getNativeGuid(), volume.getId());
log.error(message, ce);
ServiceError error = DeviceControllerErrors.cinder.operationFailed("doExpandVolume",
ce.getMessage());
taskCompleter.error(dbClient, error);
} catch (final Exception e) {
log.error("Problem in doExpandVolume: ", e);
ServiceError serviceError = DeviceControllerErrors.cinder.operationFailed("doExpandVolume", e.getMessage());
taskCompleter.error(dbClient, serviceError);
}
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doDeleteVolumes
* (com.emc.storageos.db.client.model.StorageSystem,
* java.lang.String,
* java.util.List,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doDeleteVolumes(StorageSystem storageSystem, String opId,
List<Volume> volumes, TaskCompleter taskCompleter) throws DeviceControllerException {
try {
List<String> volumeNativeIdsToDelete = new ArrayList<String>(volumes.size());
List<String> volumeLabels = new ArrayList<String>(volumes.size());
StringBuilder logMsgBuilder = new StringBuilder(
String.format("Delete Volume Start - Array:%s", storageSystem.getSerialNumber()));
log.info(logMsgBuilder.toString());
MultiVolumeTaskCompleter multiVolumeTaskCompleter = (MultiVolumeTaskCompleter) taskCompleter;
CinderEndPointInfo ep = CinderUtils.getCinderEndPoint(storageSystem.getActiveProviderURI(), dbClient);
log.info("Getting the cinder APi for the provider with id " + storageSystem.getActiveProviderURI());
CinderApi cinderApi = cinderApiFactory.getApi(storageSystem.getActiveProviderURI(), ep);
for (Volume volume : volumes) {
logMsgBuilder.append(String.format("%nVolume:%s", volume.getLabel()));
try {
// Check if the volume is present on the back-end device
cinderApi.showVolume(volume.getNativeId());
} catch (CinderException ce) {
// This means, the volume is not present on the back-end device
log.info(String.format("Volume %s already deleted: ", volume.getNativeId()));
volume.setInactive(true);
dbClient.persistObject(volume);
VolumeTaskCompleter deleteTaskCompleter = multiVolumeTaskCompleter.skipTaskCompleter(volume.getId());
deleteTaskCompleter.ready(dbClient);
continue;
}
volumeNativeIdsToDelete.add(volume.getNativeId());
volumeLabels.add(volume.getLabel());
// cleanup if there are any snapshots created for a volume
cleanupAnyBackupSnapshots(volume, cinderApi);
}
// Now - trigger the delete
if (!multiVolumeTaskCompleter.isVolumeTaskCompletersEmpty()) {
cinderApi.deleteVolumes(volumeNativeIdsToDelete.toArray(new String[] {}));
ControllerServiceImpl.enqueueJob(new QueueJob(
new CinderDeleteVolumeJob(volumeNativeIdsToDelete.get(0), volumeLabels.get(0),
volumes.get(0).getStorageController(), CinderConstants.ComponentType.volume.name(), ep, taskCompleter)));
} else {
// If we are here, there are no volumes to delete, we have
// invoked ready() for the VolumeDeleteCompleter, and told
// the multiVolumeTaskCompleter to skip these completers.
// In this case, the multiVolumeTaskCompleter complete()
// method will not be invoked and the result is that the
// workflow that initiated this delete request will never
// be updated. So, here we just call complete() on the
// multiVolumeTaskCompleter to ensure the workflow status is
// updated.
multiVolumeTaskCompleter.ready(dbClient);
}
} catch (Exception e) {
log.error("Problem in doDeleteVolume: ", e);
ServiceError error = DeviceControllerErrors.cinder.operationFailed("doDeleteVolume", e.getMessage());
taskCompleter.error(dbClient, error);
}
StringBuilder logMsgBuilder = new StringBuilder(String.format(
"Delete Volume End - Array: %s", storageSystem.getSerialNumber()));
for (Volume volume : volumes) {
logMsgBuilder.append(String.format("%nVolume:%s", volume.getLabel()));
}
log.info(logMsgBuilder.toString());
}
/**
* Removes all volume snapshots
*
* @param volume
* @param cinderApi
* @throws Exception
*/
private void cleanupAnyBackupSnapshots(Volume volume, CinderApi cinderApi) throws Exception {
// Get all snapshots of a volume
List<String> snapshotIdsList = cinderApi.listSnapshotsForVolume(volume.getNativeId());
String snapIdToRemove = "";
if (!snapshotIdsList.isEmpty()) {
// Invoke Delete for all volume snapshots
for (String snapId : snapshotIdsList) {
cinderApi.deleteSnapshot(snapId);
}
// Check the deletion status before returning to the caller.
// Return only after snapshots are deleted.
boolean isWait = true;
while (true) {
if (isWait) {
// Wait for 10 seconds before checking the status as
// deletion will take some time to complete.
try {
Thread.sleep(SNAPSHOT_DELETE_STATUS_CHECK_SLEEP_TIME);
} catch (InterruptedException e) {
log.error("Snapshot deletion check waiting thread interrupted", e);
}
}
for (String snapId : snapshotIdsList) {
try {
cinderApi.getTaskStatus(snapId, CinderConstants.ComponentType.snapshot.name());
isWait = true;
} catch (CinderException e) {
// This means the snapshot was deleted, remove it from
// the status check list
snapIdToRemove = snapId;
isWait = false; // If one of the snapshot in the list is deleted, its possible that others are also deleted
// so dont wait in the next cycle.
break; // break the for loop
}
}
// remove the snapId which is deleted from the backend
if (!snapIdToRemove.isEmpty()) {
snapshotIdsList.remove(snapIdToRemove);
snapIdToRemove = ""; // reset
}
// break the while loop on list empty
if (snapshotIdsList.isEmpty()) {
break;
}
}
}
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportCreate
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.ExportMask,
* java.util.Map, java.util.List,
* java.util.List,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExportCreate(StorageSystem storage,
ExportMask exportMask, Map<URI, Integer> volumeMap,
List<Initiator> initiators, List<URI> targets,
TaskCompleter taskCompleter) throws DeviceControllerException {
log.info("{} doExportCreate START ...", storage.getSerialNumber());
VolumeURIHLU[] volumeLunArray = ControllerUtils.getVolumeURIHLUArray(storage.getSystemType(), volumeMap, dbClient);
_exportMaskOperationsHelper.createExportMask(storage, exportMask.getId(), volumeLunArray, targets, initiators, taskCompleter);
log.info("{} doExportCreate END ...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportDelete(
* com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.ExportMask,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExportDelete(StorageSystem storage,
ExportMask exportMask, List<URI> volumeURIs, List<URI> initiatorURIs, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("{} doExportDelete START ...", storage.getSerialNumber());
_exportMaskOperationsHelper.deleteExportMask(storage, exportMask.getId(), new ArrayList<URI>(),
new ArrayList<URI>(), new ArrayList<Initiator>(), taskCompleter);
log.info("{} doExportDelete END ...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportAddVolume
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.ExportMask,
* java.net.URI, java.lang.Integer,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExportAddVolume(StorageSystem storage, ExportMask exportMask,
URI volume, Integer lun, List<Initiator> initiators, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("{} doExportAddVolume START ...", storage.getSerialNumber());
Map<URI, Integer> map = new HashMap<URI, Integer>();
map.put(volume, lun);
VolumeURIHLU[] volumeLunArray = ControllerUtils.getVolumeURIHLUArray(storage.getSystemType(), map, dbClient);
_exportMaskOperationsHelper.addVolumes(storage, exportMask.getId(), volumeLunArray, initiators, taskCompleter);
log.info("{} doExportAddVolume END ...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportAddVolumes
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.ExportMask,
* java.util.Map, com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExportAddVolumes(StorageSystem storage,
ExportMask exportMask, List<Initiator> initiators,
Map<URI, Integer> volumes, TaskCompleter taskCompleter) throws DeviceControllerException {
log.info("{} doExportAddVolume START ...", storage.getSerialNumber());
VolumeURIHLU[] volumeLunArray = ControllerUtils.getVolumeURIHLUArray(storage.getSystemType(), volumes, dbClient);
_exportMaskOperationsHelper.addVolumes(storage, exportMask.getId(),
volumeLunArray, initiators, taskCompleter);
log.info("{} doExportAddVolume END ...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportRemoveVolume
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.ExportMask,
* java.net.URI, com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExportRemoveVolume(StorageSystem storage,
ExportMask exportMask, URI volume, List<Initiator> initiators, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("{} doExportRemoveVolume START ...", storage.getSerialNumber());
_exportMaskOperationsHelper.removeVolumes(storage, exportMask.getId(), Arrays.asList(volume), initiators, taskCompleter);
log.info("{} doExportRemoveVolume END ...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportRemoveVolumes
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.ExportMask,
* java.util.List, com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExportRemoveVolumes(StorageSystem storage,
ExportMask exportMask, List<URI> volumes,
List<Initiator> initiators, TaskCompleter taskCompleter) throws DeviceControllerException {
log.info("{} doExportRemoveVolume START ...", storage.getSerialNumber());
_exportMaskOperationsHelper.removeVolumes(storage, exportMask.getId(), volumes,
initiators, taskCompleter);
log.info("{} doExportRemoveVolume END ...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportAddInitiator
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.ExportMask,
* com.emc.storageos.db.client.model.Initiator,
* java.util.List, com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExportAddInitiator(StorageSystem storage,
ExportMask exportMask, List<URI> volumeURIs, Initiator initiator,
List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException {
log.info("{} doExportAddInitiator START ...", storage.getSerialNumber());
_exportMaskOperationsHelper.addInitiators(storage, exportMask.getId(), volumeURIs, Arrays.asList(initiator),
targets, taskCompleter);
log.info("{} doExportAddInitiator END ...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportAddInitiators
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.ExportMask,
* java.util.List,
* java.util.List,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExportAddInitiators(StorageSystem storage,
ExportMask exportMask, List<URI> volumeURIs,
List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("{} doExportAddInitiator START ...", storage.getSerialNumber());
_exportMaskOperationsHelper.addInitiators(storage, exportMask.getId(), volumeURIs,
initiators, targets, taskCompleter);
log.info("{} doExportAddInitiator END ...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportRemoveInitiator
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.ExportMask,
* com.emc.storageos.db.client.model.Initiator,
* java.util.List, com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExportRemoveInitiator(StorageSystem storage,
ExportMask exportMask, List<URI> volumes, Initiator initiator,
List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException {
log.info("{} doExportRemoveInitiator START ...", storage.getSerialNumber());
_exportMaskOperationsHelper.removeInitiators(storage, exportMask.getId(),
volumes, Arrays.asList(initiator), targets, taskCompleter);
log.info("{} doExportRemoveInitiator END ...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportRemoveInitiators
* (com.emc.storageos.db.client.model.StorageSystem,
* com.emc.storageos.db.client.model.ExportMask,
* java.util.List, java.util.List,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doExportRemoveInitiators(StorageSystem storage,
ExportMask exportMask, List<URI> volumes,
List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("{} doExportRemoveInitiator START ...", storage.getSerialNumber());
_exportMaskOperationsHelper.removeInitiators(storage, exportMask.getId(),
volumes, initiators, targets, taskCompleter);
log.info("{} doExportRemoveInitiator END ...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doCreateSnapshot
* (com.emc.storageos.db.client.model.StorageSystem,
* java.util.List,
* java.lang.Boolean,
* java.lang.Boolean,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doCreateSnapshot(StorageSystem storage, List<URI> snapshotList,
Boolean createInactive, Boolean readOnly, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.debug("In CinderStorageDevice.doCreateSnapshot method.");
boolean operationFailed = false;
StringBuilder logMsgBuilder = new StringBuilder(String.format(
"Create Snapshot Start - Array:%s, ",
storage.getSerialNumber()));
BlockSnapshot snapshot = null;
try {
snapshot = dbClient.queryObject(BlockSnapshot.class, snapshotList.get(0));
Volume volume = dbClient.queryObject(Volume.class, snapshot.getParent());
logMsgBuilder.append(String.format("%nSnapshot:%s for Volume %s",
snapshot.getLabel(), volume.getLabel()));
log.info(logMsgBuilder.toString());
CinderEndPointInfo endPoint = CinderUtils.getCinderEndPoint(storage.getActiveProviderURI(), dbClient);
CinderApi cinderApi = cinderApiFactory.getApi(storage.getActiveProviderURI(), endPoint);
String snapshotID = cinderApi.createSnapshot(volume.getNativeId(), snapshot.getLabel());
if (snapshotID != null) {
CinderJob createSnapshotJob = new CinderSnapshotCreateJob(snapshotID, snapshot.getLabel(),
volume.getStorageController(), CinderConstants.ComponentType.snapshot.name(), endPoint, taskCompleter);
ControllerServiceImpl.enqueueJob(new QueueJob(createSnapshotJob));
}
} catch (Exception e) {
String message = String.format(
"Exception when trying to create snapshot(s) on array %s",
storage.getSerialNumber());
log.error(message, e);
ServiceError error = DeviceControllerErrors.cinder.operationFailed("doCreateSnapshot",
e.getMessage());
taskCompleter.error(dbClient, error);
operationFailed = true;
}
if (operationFailed && null != snapshot) {
snapshot.setInactive(true);
dbClient.persistObject(snapshot);
}
logMsgBuilder = new StringBuilder(String.format(
"Create Snapshot End - Array:%s, ",
storage.getSerialNumber()));
log.info(logMsgBuilder.toString());
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doDeleteSnapshot
* (com.emc.storageos.db.client.model.StorageSystem,
* java.net.URI, com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doDeleteSnapshot(StorageSystem storageSystem, URI snapshotURI,
TaskCompleter taskCompleter) throws DeviceControllerException {
try {
StringBuilder logMsgBuilder = new StringBuilder(String.format(
"Delete Snapshot Start - Array:%s", storageSystem.getSerialNumber()));
log.info(logMsgBuilder.toString());
BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, snapshotURI);
CinderEndPointInfo ep = CinderUtils.getCinderEndPoint(storageSystem.getActiveProviderURI(), dbClient);
log.info("Getting the cinder APi for the provider with id " + storageSystem.getActiveProviderURI());
CinderApi cinderApi = cinderApiFactory.getApi(storageSystem.getActiveProviderURI(), ep);
try {
cinderApi.showSnapshot(snapshot.getId().toString());
} catch (CinderException ce) {
// This means, the snapshot is not present on the back-end device
log.info(String.format("Snapshot %s already deleted: ", snapshot.getNativeId()));
snapshot.setInactive(true);
dbClient.updateObject(snapshot);
taskCompleter.ready(dbClient);
}
// Now - trigger the delete
cinderApi.deleteSnapshot(snapshot.getNativeId().toString());
ControllerServiceImpl.enqueueJob(new QueueJob(
new CinderSnapshotDeleteJob(snapshot.getNativeId(), snapshot.getLabel(),
storageSystem.getId(), CinderConstants.ComponentType.snapshot.name(), ep, taskCompleter)));
} catch (Exception e) {
log.error("Problem in doDeleteVolume: ", e);
ServiceError error = DeviceControllerErrors.cinder.operationFailed(
"doDeleteSnapshot", e.getMessage());
taskCompleter.error(dbClient, error);
}
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doCreateClone
* (com.emc.storageos.db.client.model.StorageSystem,
* java.net.URI,
* java.net.URI,
* java.lang.Boolean,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doCreateClone(StorageSystem storageSystem, URI sourceVolume,
URI cloneVolume, Boolean createInactive,
TaskCompleter taskCompleter) {
this.cloneOperations.createSingleClone(storageSystem, sourceVolume,
cloneVolume, createInactive,
taskCompleter);
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doDetachClone
* (com.emc.storageos.db.client.model.StorageSystem,
* java.net.URI,
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doDetachClone(StorageSystem storage, URI cloneVolume,
TaskCompleter taskCompleter) {
this.cloneOperations.detachSingleClone(storage, cloneVolume, taskCompleter);
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#findExportMasks
* (com.emc.storageos.db.client.model.StorageSystem, java.util.List, boolean)
*/
@Override
public Map<String, Set<URI>> findExportMasks(StorageSystem storage,
List<String> initiatorNames, boolean mustHaveAllPorts) throws DeviceControllerException {
return _exportMaskOperationsHelper.findExportMasks(storage, initiatorNames, mustHaveAllPorts);
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#refreshExportMask
* (com.emc.storageos.db.client.model.StorageSystem, com.emc.storageos.db.client.model.ExportMask)
*/
@Override
public ExportMask refreshExportMask(StorageSystem storage, ExportMask mask) throws DeviceControllerException {
return _exportMaskOperationsHelper.refreshExportMask(storage, mask);
}
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.BlockStorageDevice#doActivateFullCopy
* (com.emc.storageos.db.client.model.StorageSystem, java.net.URI, com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void doActivateFullCopy(StorageSystem storageSystem, URI fullCopy,
TaskCompleter completer) {
this.cloneOperations.activateSingleClone(storageSystem, fullCopy, completer);
}
@Override
public boolean validateStorageProviderConnection(String ipAddress, Integer portNumber) {
return true;
}
@Override
public void doWaitForSynchronized(Class<? extends BlockObject> clazz,
StorageSystem storageObj, URI target, TaskCompleter completer) {
// Do nothing - cinder API does not have API to synchronize copies
// This has to do nothing for VPLEX+Cinder case to pass for the
// volume clone.
log.info("Nothing to do here. Cinder does not require a wait for synchronization");
completer.ready(dbClient);
}
@Override
public void doCreateConsistencyGroup(StorageSystem storage,
URI consistencyGroup, String replicationGroupName, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("{} doCreateConsistencyGroup START ...", storage.getSerialNumber());
taskCompleter.ready(dbClient);
log.info("{} doCreateConsistencyGroup END ...", storage.getSerialNumber());
}
@Override
public void doDeleteConsistencyGroup(StorageSystem storage,
URI consistencyGroup, String replicationGroupName, Boolean keepRGName, Boolean markInactive, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("{} doDeleteConsistencyGroup START ...", storage.getSerialNumber());
taskCompleter.ready(dbClient);
log.info("{} doDeleteConsistencyGroup START ...", storage.getSerialNumber());
}
@Override
public void doDeleteConsistencyGroup(StorageSystem storage, final URI consistencyGroupId,
String replicationGroupName, Boolean keepRGName, Boolean markInactive,
String sourceReplicationGroup, final TaskCompleter taskCompleter) throws DeviceControllerException {
doDeleteConsistencyGroup(storage, consistencyGroupId, replicationGroupName, keepRGName, markInactive, taskCompleter);
}
@Override
public void doAddToConsistencyGroup(StorageSystem storage,
URI consistencyGroupId, String replicationGroupName, List<URI> blockObjects,
TaskCompleter taskCompleter) throws DeviceControllerException {
log.info("{} doAddToConsistencyGroup START ...", storage.getSerialNumber());
taskCompleter.ready(dbClient);
log.info("{} doAddToConsistencyGroup END ...", storage.getSerialNumber());
}
@Override
public void doRemoveFromConsistencyGroup(StorageSystem storage,
URI consistencyGroupId, List<URI> blockObjects,
TaskCompleter taskCompleter) throws DeviceControllerException {
log.info("{} doRemoveFromConsistencyGroup START ...", storage.getSerialNumber());
taskCompleter.ready(dbClient);
log.info("{} doRemoveFromConsistencyGroup END ...", storage.getSerialNumber());
}
}