/*
* Copyright (c) 2016 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.externaldevice;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.emc.storageos.storagedriver.HostExportInfo;
import com.emc.storageos.storagedriver.model.StorageBlockObject;
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.constraint.AlternateIdConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.AutoTieringPolicy;
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.ExportMask;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.StoragePool;
import com.emc.storageos.db.client.model.StorageProvider;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.StringSet;
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.db.exceptions.DatabaseException;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.hds.HDSConstants;
import com.emc.storageos.storagedriver.AbstractStorageDriver;
import com.emc.storageos.storagedriver.BlockStorageDriver;
import com.emc.storageos.storagedriver.DriverTask;
import com.emc.storageos.storagedriver.LockManager;
import com.emc.storageos.storagedriver.Registry;
import com.emc.storageos.storagedriver.StorageDriver;
import com.emc.storageos.storagedriver.impl.LockManagerImpl;
import com.emc.storageos.storagedriver.impl.RegistryImpl;
import com.emc.storageos.storagedriver.model.StorageObject;
import com.emc.storageos.storagedriver.model.StorageVolume;
import com.emc.storageos.storagedriver.model.VolumeClone;
import com.emc.storageos.storagedriver.model.VolumeConsistencyGroup;
import com.emc.storageos.storagedriver.model.VolumeSnapshot;
import com.emc.storageos.storagedriver.storagecapabilities.AutoTieringPolicyCapabilityDefinition;
import com.emc.storageos.storagedriver.storagecapabilities.CapabilityInstance;
import com.emc.storageos.storagedriver.storagecapabilities.CommonStorageCapabilities;
import com.emc.storageos.storagedriver.storagecapabilities.DataStorageServiceOption;
import com.emc.storageos.storagedriver.storagecapabilities.DeduplicationCapabilityDefinition;
import com.emc.storageos.storagedriver.storagecapabilities.StorageCapabilities;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.volumecontroller.ControllerLockingService;
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.NativeGUIDGenerator;
import com.emc.storageos.volumecontroller.impl.VolumeURIHLU;
import com.emc.storageos.volumecontroller.impl.externaldevice.job.CreateGroupCloneExternalDeviceJob;
import com.emc.storageos.volumecontroller.impl.externaldevice.job.CreateVolumeCloneExternalDeviceJob;
import com.emc.storageos.volumecontroller.impl.externaldevice.job.ExpandVolumeExternalDeviceJob;
import com.emc.storageos.volumecontroller.impl.externaldevice.job.RestoreFromCloneExternalDeviceJob;
import com.emc.storageos.volumecontroller.impl.externaldevice.job.RestoreFromGroupCloneExternalDeviceJob;
import com.emc.storageos.volumecontroller.impl.externaldevice.job.RestoreFromSnapshotExternalDeviceJob;
import com.emc.storageos.volumecontroller.impl.job.QueueJob;
import com.emc.storageos.volumecontroller.impl.plugins.ExternalDeviceCommunicationInterface;
import com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations;
import com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils;
import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
/**
* BlockStorageDevice implementation for device drivers.
* Note: If references to driver model instances are used in internal hash maps, wrap collections in unmodifiable view when calling
* driver. For example: use Collections.unmodifiableList(modifiableList) for List collections.
*/
public class ExternalBlockStorageDevice extends DefaultBlockStorageDevice {
private static Logger _log = LoggerFactory.getLogger(ExternalBlockStorageDevice.class);
// Storage drivers for block devices
private Map<String, AbstractStorageDriver> drivers;
private DbClient dbClient;
private ControllerLockingService locker;
private ExportMaskOperations exportMaskOperationsHelper;
// Initialized drivers map
private static Map<String, BlockStorageDriver> blockDrivers = new HashMap<>();
public void setDbClient(DbClient dbClient) {
this.dbClient = dbClient;
}
public void setLocker(ControllerLockingService locker) {
this.locker = locker;
}
public void setDrivers(Map<String, AbstractStorageDriver> drivers) {
this.drivers = drivers;
}
public Map<String, AbstractStorageDriver> getDrivers() {
return drivers;
}
public void setExportMaskOperationsHelper(ExportMaskOperations exportMaskOperationsHelper) {
this.exportMaskOperationsHelper = exportMaskOperationsHelper;
}
public synchronized BlockStorageDriver getDriver(String driverType) {
// look up driver
BlockStorageDriver storageDriver = blockDrivers.get(driverType);
if (storageDriver != null) {
return storageDriver;
} else {
// init driver
AbstractStorageDriver driver = drivers.get(driverType);
if (driver == null) {
_log.error("No driver entry defined for device type: {} . ", driverType);
throw ExternalDeviceException.exceptions.noDriverDefinedForDevice(driverType);
}
init(driver);
blockDrivers.put(driverType, (BlockStorageDriver)driver);
return (BlockStorageDriver)driver;
}
}
private void init(AbstractStorageDriver driver) {
Registry driverRegistry = RegistryImpl.getInstance(dbClient);
driver.setDriverRegistry(driverRegistry);
LockManager lockManager = LockManagerImpl.getInstance(locker);
driver.setLockManager(lockManager);
driver.setSdkVersionNumber(StorageDriver.SDK_VERSION_NUMBER);
}
@Override
public void doCreateVolumes(StorageSystem storageSystem, StoragePool storagePool,
String opId, List<Volume> volumes,
VirtualPoolCapabilityValuesWrapper capabilities,
TaskCompleter taskCompleter) throws DeviceControllerException {
List<StorageVolume> driverVolumes = new ArrayList<>();
Map<StorageVolume, Volume> driverVolumeToVolumeMap = new HashMap<>();
Set<URI> consistencyGroups = new HashSet<>();
StorageCapabilities storageCapabilities = null;
DriverTask task = null;
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
try {
for (Volume volume : volumes) {
if (storageCapabilities == null) {
// All volumes created in a request will have the same capabilities.
storageCapabilities = new StorageCapabilities();
addAutoTieringPolicyCapability(storageCapabilities, volume.getAutoTieringPolicyUri());
addDeduplicationCapability(storageCapabilities, volume.getIsDeduplicated());
}
StorageVolume driverVolume = new StorageVolume();
driverVolume.setStorageSystemId(storageSystem.getNativeId());
driverVolume.setStoragePoolId(storagePool.getNativeId());
driverVolume.setRequestedCapacity(volume.getCapacity());
driverVolume.setThinlyProvisioned(volume.getThinlyProvisioned());
driverVolume.setDisplayName(volume.getLabel());
if (!NullColumnValueGetter.isNullURI(volume.getConsistencyGroup())) {
BlockConsistencyGroup cg = dbClient.queryObject(BlockConsistencyGroup.class, volume.getConsistencyGroup());
driverVolume.setConsistencyGroup(cg.getNativeId());
}
driverVolumes.add(driverVolume);
driverVolumeToVolumeMap.put(driverVolume, volume);
}
// Call driver
task = driver.createVolumes(Collections.unmodifiableList(driverVolumes), storageCapabilities);
// todo: need to implement support for async case.
if (task.getStatus() == DriverTask.TaskStatus.READY || task.getStatus() == DriverTask.TaskStatus.PARTIALLY_FAILED ) {
updateVolumesWithDriverVolumeInfo(dbClient, driverVolumeToVolumeMap, consistencyGroups);
dbClient.updateObject(driverVolumeToVolumeMap.values());
updateConsistencyGroupsWithStorageSystem(consistencyGroups, storageSystem);
String msg = String.format("doCreateVolumes -- Created volumes: %s .", task.getMessage());
_log.info(msg);
taskCompleter.ready(dbClient);
} else {
// Set volumes to inactive state
for (Volume volume : volumes) {
volume.setInactive(true);
}
dbClient.updateObject(volumes);
String errorMsg = String.format("doCreateVolumes -- Failed to create volumes: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.createVolumesFailed("doCreateVolumes", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
_log.error("doCreateVolumes -- Failed to create volumes. ", e);
ServiceError serviceError = ExternalDeviceException.errors.createVolumesFailed("doCreateVolumes", e.getMessage());
taskCompleter.error(dbClient, serviceError);
} finally {
try {
if (task == null || isTaskInTerminalState(task.getStatus())) {
updateStoragePoolCapacity(storagePool, storageSystem,
URIUtil.toUris(volumes), dbClient);
}
} catch (Exception ex) {
_log.error("Failed to update storage pool {} after create volumes operation completion.", storagePool.getId(), ex);
}
}
}
/**
* Create the auto tiering policy capability and add it to the passed
* storage capabilities
*
* @param storageCapabilities A reference to all storage capabilities.
* @param autoTieringPolicyURI The URI of the AutoTieringPolicy or null.
*/
private void addAutoTieringPolicyCapability(StorageCapabilities storageCapabilities, URI autoTieringPolicyURI) {
if (!NullColumnValueGetter.isNullURI(autoTieringPolicyURI)) {
AutoTieringPolicy autoTieringPolicy = dbClient.queryObject(AutoTieringPolicy.class, autoTieringPolicyURI);
if (autoTieringPolicy == null) {
throw DeviceControllerException.exceptions.objectNotFound(autoTieringPolicyURI);
}
// Create the auto tiering policy capability.
AutoTieringPolicyCapabilityDefinition capabilityDefinition = new AutoTieringPolicyCapabilityDefinition();
Map<String, List<String>> capabilityProperties = new HashMap<>();
capabilityProperties.put(AutoTieringPolicyCapabilityDefinition.PROPERTY_NAME.POLICY_ID.name(),
Arrays.asList(autoTieringPolicy.getPolicyName()));
capabilityProperties.put(AutoTieringPolicyCapabilityDefinition.PROPERTY_NAME.PROVISIONING_TYPE.name(),
Arrays.asList(autoTieringPolicy.getProvisioningType()));
CapabilityInstance autoTieringCapability = new CapabilityInstance(capabilityDefinition.getId(),
autoTieringPolicy.getPolicyName(), capabilityProperties);
// Get the common capabilities for the passed storage capabilities.
// If null, create and set it.
CommonStorageCapabilities commonCapabilities = storageCapabilities.getCommonCapabilitis();
if (commonCapabilities == null) {
commonCapabilities = new CommonStorageCapabilities();
storageCapabilities.setCommonCapabilitis(commonCapabilities);
}
// Get the data storage service options for the common capabilities.
// If null, create it and set it.
List<DataStorageServiceOption> dataStorageSvcOptions = commonCapabilities.getDataStorage();
if (dataStorageSvcOptions == null) {
dataStorageSvcOptions = new ArrayList<>();
commonCapabilities.setDataStorage(dataStorageSvcOptions);
}
// Create a new data storage service option for the AutoTiering policy capability
// and add it to the list.
DataStorageServiceOption dataStorageSvcOption = new DataStorageServiceOption(Arrays.asList(autoTieringCapability));
dataStorageSvcOptions.add(dataStorageSvcOption);
}
}
/**
* Create deduplication capability and add it to the passed
* storage capabilities
*
* @param storageCapabilities reference to storage capbilities
* @param deduplication indicates if deduplication is required
*/
private void addDeduplicationCapability(StorageCapabilities storageCapabilities, Boolean deduplication) {
if (deduplication) {
// Create the deduplicated capability.
DeduplicationCapabilityDefinition capabilityDefinition = new DeduplicationCapabilityDefinition();
Map<String, List<String>> capabilityProperties = new HashMap<>();
capabilityProperties.put(DeduplicationCapabilityDefinition.PROPERTY_NAME.ENABLED.name(),
Collections.singletonList(Boolean.TRUE.toString()));
CapabilityInstance dedupCapability = new CapabilityInstance(capabilityDefinition.getId(),
capabilityDefinition.getId(), capabilityProperties);
// Get the common capabilities for the passed storage capabilities.
// If null, create and set it.
CommonStorageCapabilities commonCapabilities = storageCapabilities.getCommonCapabilitis();
if (commonCapabilities == null) {
commonCapabilities = new CommonStorageCapabilities();
storageCapabilities.setCommonCapabilitis(commonCapabilities);
}
// Get the data storage service options for the common capabilities.
// If null, create it and set it.
List<DataStorageServiceOption> dataStorageSvcOptions = commonCapabilities.getDataStorage();
if (dataStorageSvcOptions == null) {
dataStorageSvcOptions = new ArrayList<>();
commonCapabilities.setDataStorage(dataStorageSvcOptions);
}
// Create a new data storage service option for the auto tiering policy capability
// and add it to the list.
DataStorageServiceOption dataStorageSvcOption = new DataStorageServiceOption(Collections.singletonList(dedupCapability));
dataStorageSvcOptions.add(dataStorageSvcOption);
}
}
@Override
public void doExpandVolume(StorageSystem storageSystem, StoragePool storagePool,
Volume volume, Long size, TaskCompleter taskCompleter)
throws DeviceControllerException {
_log.info("Volume expand ..... Started");
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = null;
try {
// Prepare driver volume
StorageVolume driverVolume = new StorageVolume();
driverVolume.setNativeId(volume.getNativeId());
driverVolume.setDeviceLabel(volume.getDeviceLabel());
driverVolume.setStorageSystemId(storageSystem.getNativeId());
driverVolume.setStoragePoolId(storagePool.getNativeId());
driverVolume.setRequestedCapacity(volume.getCapacity());
driverVolume.setThinlyProvisioned(volume.getThinlyProvisioned());
driverVolume.setDisplayName(volume.getLabel());
driverVolume.setAllocatedCapacity(volume.getAllocatedCapacity());
driverVolume.setProvisionedCapacity(volume.getProvisionedCapacity());
driverVolume.setWwn(volume.getWWN());
// call driver
task = driver.expandVolume(driverVolume, size);
if (!isTaskInTerminalState(task.getStatus())) {
// If the task is not in a terminal state and will be completed asynchronously
// create a job to monitor the progress of the request and update the volume and
// call the completer as appropriate based on the result of the request.
ExpandVolumeExternalDeviceJob job = new ExpandVolumeExternalDeviceJob(
storageSystem.getId(), volume.getId(), task.getTaskId(), taskCompleter);
ControllerServiceImpl.enqueueJob(new QueueJob(job));
} else if (task.getStatus() == DriverTask.TaskStatus.READY) {
String msg = String.format("doExpandVolume -- Expanded volume: %s .", task.getMessage());
_log.info(msg);
ExternalDeviceUtils.updateExpandedVolume(volume, driverVolume, dbClient);
taskCompleter.ready(dbClient);
} else {
// operation failed
String errorMsg = String.format("doExpandVolume -- Failed to expand volume: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.expandVolumeFailed("doExpandVolume", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
_log.error("doExpandVolume -- Failed to expand volume. ", e);
ServiceError serviceError = ExternalDeviceException.errors.expandVolumeFailed("doExpandVolume", e.getMessage());
taskCompleter.error(dbClient, serviceError);
} finally {
try {
if (task == null || isTaskInTerminalState(task.getStatus())) {
updateStoragePoolCapacity(storagePool, storageSystem,
URIUtil.toUris(Collections.singletonList(volume)), dbClient);
}
} catch (Exception ex) {
_log.error("Failed to update storage pool {} after expand volume operation completion.", storagePool.getId(), ex);
}
}
}
/**
* {@inheritDoc}
*
*/
@Override
public void doDeleteVolumes(StorageSystem storageSystem, String opId,
List<Volume> volumes, TaskCompleter taskCompleter) throws DeviceControllerException {
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
List<Volume> deletedVolumes = new ArrayList<>();
List<String> failedToDeleteVolumes = new ArrayList<>();
List<Volume> deletedClones = new ArrayList<>();
List<String> failedToDeleteClones = new ArrayList<>();
boolean exception = false;
StringBuffer errorMsgForVolumes = new StringBuffer();
StringBuffer errorMsgForClones = new StringBuffer();
try {
for (Volume volume : volumes) {
DriverTask task = null;
// Check if this is regular volume or this is volume clone
if (!NullColumnValueGetter.isNullURI(volume.getAssociatedSourceVolume())) {
// this is clone
_log.info("Deleting volume clone on storage system {}, clone: {} .",
storageSystem.getNativeId(), volume.getNativeId());
BlockObject sourceVolume = BlockObject.fetch(dbClient, volume.getAssociatedSourceVolume());
VolumeClone driverClone = new VolumeClone();
driverClone.setStorageSystemId(storageSystem.getNativeId());
driverClone.setNativeId(volume.getNativeId());
driverClone.setDeviceLabel(volume.getDeviceLabel());
driverClone.setParentId(sourceVolume.getNativeId());
driverClone.setConsistencyGroup(volume.getReplicationGroupInstance());
// check for exports
if (hasExports(driver, driverClone)) {
failedToDeleteClones.add(volume.getNativeId());
String errorMsgClone = String.format("Cannot delete clone %s on storage system %s, clone has exports on array.",
driverClone.getNativeId(), storageSystem.getNativeId());
_log.error(errorMsgClone);
errorMsgForClones.append(errorMsgClone +"\n");
continue;
}
task = driver.deleteVolumeClone(driverClone);
} else {
// this is regular volume
_log.info("Deleting volume on storage system {}, volume: {} .",
storageSystem.getNativeId(), volume.getNativeId());
StorageVolume driverVolume = new StorageVolume();
driverVolume.setStorageSystemId(storageSystem.getNativeId());
driverVolume.setNativeId(volume.getNativeId());
driverVolume.setDeviceLabel(volume.getDeviceLabel());
driverVolume.setConsistencyGroup(volume.getReplicationGroupInstance());
// check for exports
if (hasExports(driver, driverVolume)) {
failedToDeleteVolumes.add(volume.getNativeId());
String errorMsgVolume = String.format("Cannot delete volume %s on storage system %s, volume has exports on array.",
driverVolume.getNativeId(), storageSystem.getNativeId());
_log.error(errorMsgVolume);
errorMsgForVolumes.append(errorMsgVolume + "\n");
continue;
}
task = driver.deleteVolume(driverVolume);
}
if (task.getStatus() == DriverTask.TaskStatus.READY) {
volume.setInactive(true);
if (volume.getAssociatedSourceVolume() != null) {
deletedClones.add(volume);
} else {
deletedVolumes.add(volume);
}
} else {
if (volume.getAssociatedSourceVolume() != null) {
failedToDeleteClones.add(volume.getNativeId());
} else {
failedToDeleteVolumes.add(volume.getNativeId());
}
}
}
} catch (Exception e) {
exception = true;
_log.error("doDeleteVolumes -- Failed to delete volumes. ", e);
ServiceError serviceError = ExternalDeviceException.errors.deleteVolumesFailed("doDeleteVolumes", e.getMessage());
taskCompleter.error(dbClient, serviceError);
} finally {
if (!deletedVolumes.isEmpty()){
_log.info("Deleted volumes on storage system {}, volumes: {} .",
storageSystem.getNativeId(), deletedVolumes.toString());
dbClient.updateObject(deletedVolumes);
}
if (!deletedClones.isEmpty()){
_log.info("Deleted volume clones on storage system {}, clones: {} .",
storageSystem.getNativeId(), deletedClones.toString());
dbClient.updateObject(deletedClones);
}
if(!(failedToDeleteVolumes.isEmpty() && failedToDeleteClones.isEmpty())) {
if(!failedToDeleteVolumes.isEmpty()) {
String errorMsgVolumes = String.format("Failed to delete volumes on storage system %s, volumes: %s . ",
storageSystem.getNativeId(), failedToDeleteVolumes.toString());
_log.error(errorMsgVolumes);
} else {
String errorMsgClones = String.format("Failed to delete volume clones on storage system %s, clones: %s .",
storageSystem.getNativeId(), failedToDeleteClones.toString());
_log.error(errorMsgClones);
}
ServiceError serviceError = ExternalDeviceException.errors.deleteVolumesFailed("doDeleteVolumes",
errorMsgForVolumes.append(errorMsgForClones).toString());
taskCompleter.error(dbClient, serviceError);
} else if (!exception){
taskCompleter.ready(dbClient);
}
}
}
@Override
public void doCreateSingleSnapshot(StorageSystem storage, List<URI> snapshotList, Boolean createInactive, Boolean readOnly, TaskCompleter taskCompleter) throws DeviceControllerException {
super.doCreateSingleSnapshot(storage, snapshotList, createInactive, readOnly, taskCompleter);
}
@Override
public void doCreateSnapshot(StorageSystem storage, List<URI> snapshotList, Boolean createInactive, Boolean readOnly, TaskCompleter taskCompleter)
throws DeviceControllerException {
try {
Iterator<BlockSnapshot> snapshots = dbClient.queryIterativeObjects(BlockSnapshot.class, snapshotList);
List<BlockSnapshot> blockSnapshots = new ArrayList<>();
while (snapshots.hasNext()) {
blockSnapshots.add(snapshots.next());
}
if (ControllerUtils.checkSnapshotsInConsistencyGroup(blockSnapshots, dbClient, taskCompleter)) {
// all snapshots should be for the same CG (enforced by controller)
createGroupSnapshots(storage, blockSnapshots, createInactive, readOnly, taskCompleter);
} else {
createVolumeSnapshots(storage, blockSnapshots, createInactive, readOnly, taskCompleter);
}
} catch (DatabaseException e) {
String message = String.format("IO exception when trying to create snapshot(s) on array %s",
storage.getSerialNumber());
_log.error(message, e);
ServiceError error = ExternalDeviceException.errors.createSnapshotsFailed("doCreateSnapshot", e.getMessage());
taskCompleter.error(dbClient, error);
}
}
@Override
public void doRestoreFromSnapshot(StorageSystem storageSystem, URI volume,
URI snapshot, TaskCompleter taskCompleter)
throws DeviceControllerException {
String storageSystemNativeId = storageSystem.getNativeId();
_log.info("Snapshot Restore..... Started");
BlockConsistencyGroup parentVolumeConsistencyGroup = null;
try {
List<BlockSnapshot> snapshotsToRestore = new ArrayList<>();
BlockSnapshot blockSnapshot = dbClient.queryObject(BlockSnapshot.class, snapshot);
List<BlockSnapshot> groupSnapshots = ControllerUtils.getSnapshotsPartOfReplicationGroup(blockSnapshot, dbClient);
if (groupSnapshots.size() > 1 &&
ControllerUtils.checkSnapshotsInConsistencyGroup(Arrays.asList(blockSnapshot), dbClient, taskCompleter)) {
// make sure we restore only snapshots from the same consistency group
for (BlockSnapshot snap : groupSnapshots) {
if (snap.getConsistencyGroup().equals(blockSnapshot.getConsistencyGroup())) {
snapshotsToRestore.add(snap);
}
}
URI cgUri = blockSnapshot.getConsistencyGroup();
parentVolumeConsistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, cgUri);
_log.info("Restore group snapshot: group {}, snapshot set: {}, snapshots to restore: "
+ Joiner.on("\t").join(snapshotsToRestore), parentVolumeConsistencyGroup.getNativeId(),
blockSnapshot.getReplicationGroupInstance());
} else {
Volume sourceVolume = getSnapshotParentVolume(blockSnapshot);
snapshotsToRestore.add(blockSnapshot);
_log.info("Restore single volume snapshot: volume {}, snapshot: {}", sourceVolume.getNativeId(), blockSnapshot.getNativeId());
}
// Prepare driver snapshots
List<VolumeSnapshot> driverSnapshots = new ArrayList<>();
for (BlockSnapshot snap : snapshotsToRestore) {
VolumeSnapshot driverSnapshot = new VolumeSnapshot();
Volume sourceVolume = getSnapshotParentVolume(snap);
driverSnapshot.setParentId(sourceVolume.getNativeId());
driverSnapshot.setNativeId(snap.getNativeId());
driverSnapshot.setStorageSystemId(storageSystemNativeId);
driverSnapshot.setDisplayName(snap.getLabel());
if (parentVolumeConsistencyGroup != null) {
driverSnapshot.setConsistencyGroup(snap.getReplicationGroupInstance());
}
driverSnapshots.add(driverSnapshot);
}
// Call driver to execute this request
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = driver.restoreSnapshot(driverSnapshots);
if (!isTaskInTerminalState(task.getStatus())) {
// If the task is not in a terminal state and will be completed asynchronously
// create a job to monitor the progress of the request and call the completer as
// appropriate based on the result of the request.
RestoreFromSnapshotExternalDeviceJob job = new RestoreFromSnapshotExternalDeviceJob(
storageSystem.getId(), snapshot, task.getTaskId(), taskCompleter);
ControllerServiceImpl.enqueueJob(new QueueJob(job));
} else if (task.getStatus() == DriverTask.TaskStatus.READY) {
String msg = String.format("doRestoreFromSnapshot -- Restored snapshots: %s .", task.getMessage());
_log.info(msg);
taskCompleter.ready(dbClient);
} else {
String errorMsg = String.format("doRestoreFromSnapshot -- Failed to restore from snapshots: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.restoreFromSnapshotFailed("doRestoreFromSnapshot", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
String message = String.format("IO exception when trying to restore from snapshots on array %s",
storageSystem.getSerialNumber());
_log.error(message, e);
ServiceError error = ExternalDeviceException.errors.restoreFromSnapshotFailed("doRestoreFromSnapshot", e.getMessage());
taskCompleter.error(dbClient, error);
}
_log.info("Snapshot Restore..... End");
}
@Override
public void doDeleteSnapshot(StorageSystem storage, URI snapshot,
TaskCompleter taskCompleter) throws DeviceControllerException {
try {
BlockSnapshot blockSnapshot = dbClient.queryObject(BlockSnapshot.class, snapshot);
List<BlockSnapshot> groupSnapshots = ControllerUtils.getSnapshotsPartOfReplicationGroup(blockSnapshot, dbClient);
if (!groupSnapshots.isEmpty() &&
ControllerUtils.checkSnapshotsInConsistencyGroup(Arrays.asList(blockSnapshot), dbClient, taskCompleter)) {
// make sure we delete only snapshots from the same consistency group
List<BlockSnapshot> snapshotsToDelete = new ArrayList<>();
for (BlockSnapshot snap : groupSnapshots ) {
if (snap.getConsistencyGroup().equals(blockSnapshot.getConsistencyGroup())) {
snapshotsToDelete.add(snap);
}
}
deleteGroupSnapshots(storage, snapshotsToDelete, taskCompleter);
} else {
deleteVolumeSnapshot(storage, snapshot, taskCompleter);
}
} catch (DatabaseException e) {
String message = String.format(
"IO exception when trying to delete snapshot(s) on array %s", storage.getSerialNumber());
_log.error(message, e);
ServiceError error = ExternalDeviceException.errors.deleteSnapshotFailed("doDeleteSnapshot", e.getMessage());
taskCompleter.error(dbClient, error);
} catch (Exception e) {
String message = String.format(
"Exception when trying to delete snapshot(s) on array %s", storage.getSerialNumber());
_log.error(message, e);
ServiceError error = ExternalDeviceException.errors.deleteSnapshotFailed("doDeleteSnapshot", e.getMessage());
taskCompleter.error(dbClient, error);
}
}
@Override
public void doCreateClone(StorageSystem storageSystem, URI volume, URI clone, Boolean createInactive,
TaskCompleter taskCompleter) {
Volume cloneObject = null;
DriverTask task = null;
try {
cloneObject = dbClient.queryObject(Volume.class, clone);
BlockObject sourceVolume = BlockObject.fetch(dbClient, volume);
VolumeClone driverClone = new VolumeClone();
if (sourceVolume instanceof Volume) {
driverClone.setSourceType(VolumeClone.SourceType.VOLUME);
} else if (sourceVolume instanceof BlockSnapshot) {
driverClone.setSourceType(VolumeClone.SourceType.SNAPSHOT);
} else {
cloneObject.setInactive(true);
dbClient.updateObject(cloneObject);
String errorMsg = String.format("doCreateClone -- Failed to create volume clone: unexpected source type %s .",
sourceVolume.getClass().getSimpleName());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.createVolumeCloneFailed("doCreateClone", errorMsg);
taskCompleter.error(dbClient, serviceError);
return;
}
// Prepare driver clone
driverClone.setParentId(sourceVolume.getNativeId());
driverClone.setStorageSystemId(storageSystem.getNativeId());
driverClone.setDisplayName(cloneObject.getLabel());
driverClone.setRequestedCapacity(cloneObject.getCapacity());
driverClone.setThinlyProvisioned(cloneObject.getThinlyProvisioned());
// Call driver
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
List<VolumeClone> driverClones = new ArrayList<>();
driverClones.add(driverClone);
task = driver.createVolumeClone(Collections.unmodifiableList(driverClones), null);
if (!isTaskInTerminalState(task.getStatus())) {
// If the task is not in a terminal state and will be completed asynchronously
// create a job to monitor the progress of the request and update the clone
// volume and call the completer as appropriate based on the result of the request.
CreateVolumeCloneExternalDeviceJob job = new CreateVolumeCloneExternalDeviceJob(
storageSystem.getId(), clone, task.getTaskId(), taskCompleter);
ControllerServiceImpl.enqueueJob(new QueueJob(job));
} else if (task.getStatus() == DriverTask.TaskStatus.READY) {
// Update clone
String msg = String.format("doCreateClone -- Created volume clone: %s .", task.getMessage());
_log.info(msg);
VolumeClone driverCloneResult = driverClones.get(0);
ExternalDeviceUtils.updateNewlyCreatedClone(cloneObject, driverCloneResult, dbClient);
taskCompleter.ready(dbClient);
} else {
cloneObject.setInactive(true);
dbClient.updateObject(cloneObject);
String errorMsg = String.format("doCreateClone -- Failed to create volume clone: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.createVolumeCloneFailed("doCreateClone", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
if (cloneObject != null) {
cloneObject.setInactive(true);
dbClient.updateObject(cloneObject);
}
_log.error("Failed to create volume clone. ", e);
ServiceError serviceError = ExternalDeviceException.errors.createVolumeCloneFailed("doCreateClone", e.getMessage());
taskCompleter.error(dbClient, serviceError);
} finally {
try {
if (task == null || isTaskInTerminalState(task.getStatus())) {
StoragePool dbPool = dbClient.queryObject(StoragePool.class, cloneObject.getPool());
updateStoragePoolCapacity(dbPool, storageSystem,
Collections.singletonList(clone), dbClient);
}
} catch (Exception ex) {
_log.error("Failed to update storage pool {} after create clone operation completion.", cloneObject.getPool(), ex);
}
}
}
@Override
public void doDisconnect(StorageSystem storageSystem){
try{
_log.info("doDisconnect {} - start", storageSystem.getId());
com.emc.storageos.storagedriver.model.StorageSystem driverStorageSystem = ExternalDeviceCommunicationInterface.initStorageSystem(storageSystem);
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = driver.stopManagement(driverStorageSystem);
if (task.getStatus() == DriverTask.TaskStatus.READY) {
_log.info("doDisconnect -- Disconnected Storage System: {}", task.getMessage());
} else {
_log.error("doDisconnect failed. ", task.getMessage());
throw ExternalDeviceException.exceptions.doDisconnectFailed("doDisconnect", task.getMessage());
}
_log.info("doDisconnect %1$s - Complete", storageSystem.getId());
} catch(Exception e){
_log.error("doDisconnect failed. ", e.getMessage());
throw ExternalDeviceException.exceptions.doDisconnectFailed("doDisconnect", e.getMessage());
}
}
@Override
public void doAddToConsistencyGroup(StorageSystem storageSystem, URI consistencyGroupId, String replicationGroupName,
List<URI> blockObjects, TaskCompleter taskCompleter) throws DeviceControllerException {
BlockConsistencyGroup consistencyGroup =null;
try {
_log.info("{} doAddToConsistencyGroup START ...", storageSystem.getSerialNumber());
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
List<Volume> volumes = dbClient.queryObject(Volume.class, blockObjects);
List<StorageVolume> driverVolumes = new ArrayList<>();
consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId);
for (Volume volume : volumes) {
StorageVolume driverVolume = new StorageVolume();
driverVolume.setStorageSystemId(storageSystem.getNativeId());
driverVolume.setNativeId(volume.getNativeId());
driverVolume.setRequestedCapacity(volume.getCapacity());
driverVolume.setThinlyProvisioned(volume.getThinlyProvisioned());
driverVolume.setConsistencyGroup(consistencyGroup.getNativeId());
driverVolume.setDisplayName(volume.getLabel());
//add them to StorageVolumes list
driverVolumes.add(driverVolume);
}
DriverTask task = driver.addVolumesToConsistencyGroup(driverVolumes, null);
_log.info("doAddToConsistencyGroup -- added volumes {} to consistency Group: {}", volumes.toString(), consistencyGroupId);
if(task.getStatus() == DriverTask.TaskStatus.READY){
for (Volume volume : volumes) {
volume.setConsistencyGroup(consistencyGroupId);
}
dbClient.updateObject(volumes);
taskCompleter.ready(dbClient);
} else {
_log.error(String.format("Add volumes to Consistency Group operation failed %s", task.getMessage()));
taskCompleter.error(dbClient, DeviceControllerException.exceptions
.failedToAddMembersToConsistencyGroup(consistencyGroup.getLabel(),
consistencyGroup.getLabel(), task.getMessage()));
}
_log.info("{} doAddVolumesToConsistencyGroup END ...", storageSystem.getSerialNumber());
} catch (Exception e) {
_log.error(String.format("Add volumes from Consistency Group operation failed %s", e.getMessage()));
taskCompleter.error(dbClient, DeviceControllerException.exceptions
.failedToAddMembersToConsistencyGroup(consistencyGroup.getLabel(),
consistencyGroup.getLabel(), e.getMessage()));
}
}
@Override
public void doRemoveFromConsistencyGroup(StorageSystem storageSystem, URI consistencyGroupId,
List<URI> blockObjects, TaskCompleter taskCompleter) throws DeviceControllerException {
BlockConsistencyGroup consistencyGroup =null;
try {
_log.info("{} doRemoveVolumesFromConsistencyGroup START ...", storageSystem.getSerialNumber());
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
List<Volume> volumes = dbClient.queryObject(Volume.class, blockObjects);
List<StorageVolume> driverVolumes = new ArrayList<>();
consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId);
for (Volume volume : volumes) {
StorageVolume driverVolume = new StorageVolume();
driverVolume.setStorageSystemId(storageSystem.getNativeId());
driverVolume.setNativeId(volume.getNativeId());
driverVolume.setRequestedCapacity(volume.getCapacity());
driverVolume.setThinlyProvisioned(volume.getThinlyProvisioned());
driverVolume.setConsistencyGroup(consistencyGroup.getNativeId());
driverVolume.setDisplayName(volume.getLabel());
//add them to StorageVolumes list
driverVolumes.add(driverVolume);
}
DriverTask task = driver.removeVolumesFromConsistencyGroup(driverVolumes, null);
_log.info("doRemoveVolumesFromConsistencyGroup -- removing volumes {} from consistency Group: {}", volumes.toString(), consistencyGroupId);
if(task.getStatus() == DriverTask.TaskStatus.READY){
for (Volume volume : volumes) {
volume.setConsistencyGroup(NullColumnValueGetter.getNullURI());
}
dbClient.updateObject(volumes);
taskCompleter.ready(dbClient);
} else {
_log.error(String.format("Remove volumes from Consistency Group operation failed %s", task.getMessage()));
taskCompleter.error(dbClient, DeviceControllerException.exceptions
.failedToRemoveMembersToConsistencyGroup(consistencyGroup.getLabel(),
consistencyGroup.getLabel(), task.getMessage()));
}
_log.info("{} doRemoveVolumesFromConsistencyGroup END ...", storageSystem.getSerialNumber());
} catch (Exception e) {
_log.error(String.format("Remove volumes from Consistency Group operation failed %s", e.getMessage()));
taskCompleter.error(dbClient, DeviceControllerException.exceptions
.failedToRemoveMembersToConsistencyGroup(consistencyGroup.getLabel(),
consistencyGroup.getLabel(), e.getMessage()));
}
}
@Override
public void doCreateGroupClone(StorageSystem storageSystem, List<URI> cloneURIs,
Boolean createInactive, TaskCompleter taskCompleter) {
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
List<VolumeClone> driverClones = new ArrayList<>();
Map<VolumeClone, Volume> driverCloneToCloneMap = new HashMap<>();
Set<URI> consistencyGroups = new HashSet<>();
List<Volume> clones = null;
DriverTask task = null;
try {
clones = dbClient.queryObject(Volume.class, cloneURIs);
// We assume here that all volumes belong to the same consistency group
URI parentUri = clones.get(0).getAssociatedSourceVolume();
Volume parentVolume = dbClient.queryObject(Volume.class, parentUri);
BlockConsistencyGroup cg = null;
if (!NullColumnValueGetter.isNullURI(parentVolume.getConsistencyGroup())) {
cg = dbClient.queryObject(BlockConsistencyGroup.class, parentVolume.getConsistencyGroup());
} else {
String errorMsg = String.format("doCreateGroupClone -- Failed to create group clone, parent volumes do not belong to consistency group." +
" Clones: %s .", cloneURIs);
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.createGroupCloneFailed("doCreateGroupClone",errorMsg);
taskCompleter.error(dbClient, serviceError);
return;
}
// Prepare driver consistency group of parent volume
VolumeConsistencyGroup driverCG = new VolumeConsistencyGroup();
driverCG.setDisplayName(cg.getLabel());
driverCG.setNativeId(cg.getNativeId());
driverCG.setStorageSystemId(storageSystem.getNativeId());
// Prepare driver clones
for (Volume clone : clones) {
URI sourceVolumeUri = clone.getAssociatedSourceVolume();
Volume sourceVolume = dbClient.queryObject(Volume.class, sourceVolumeUri);
VolumeClone driverClone = new VolumeClone();
driverClone.setParentId(sourceVolume.getNativeId());
driverClone.setStorageSystemId(storageSystem.getNativeId());
driverClone.setDisplayName(clone.getLabel());
driverClone.setRequestedCapacity(clone.getCapacity());
driverClone.setThinlyProvisioned(clone.getThinlyProvisioned());
driverClones.add(driverClone);
driverCloneToCloneMap.put(driverClone, clone);
}
// Call driver to create group snapshot
task = driver.createConsistencyGroupClone(driverCG, Collections.unmodifiableList(driverClones), null);
if (!isTaskInTerminalState(task.getStatus())) {
// If the task is not in a terminal state and will be completed asynchronously
// create a job to monitor the progress of the request and update the clone
// volume and call the completer as appropriate based on the result of the request.
CreateGroupCloneExternalDeviceJob job = new CreateGroupCloneExternalDeviceJob(
storageSystem.getId(), cloneURIs, parentVolume.getConsistencyGroup(),
task.getTaskId(), taskCompleter);
ControllerServiceImpl.enqueueJob(new QueueJob(job));
} else if (task.getStatus() == DriverTask.TaskStatus.READY) {
// Update clone object with driver data
String msg = String.format("doCreateGroupClone -- Created group clone: %s .", task.getMessage());
_log.info(msg);
List<Volume> cloneObjects = new ArrayList<>();
for (VolumeClone driverCloneResult : driverClones) {
Volume cloneObject = driverCloneToCloneMap.get(driverCloneResult);
ExternalDeviceUtils.updateNewlyCreatedGroupClone(cloneObject, driverCloneResult, parentVolume.getConsistencyGroup(), dbClient);
cloneObjects.add(cloneObject);
}
dbClient.updateObject(cloneObjects);
taskCompleter.ready(dbClient);
} else {
// Process failure
for (Volume cloneObject : clones) {
cloneObject.setInactive(true);
}
dbClient.updateObject(clones);
String errorMsg = String.format("doCreateGroupClone -- Failed to create group clone: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.createGroupCloneFailed("doCreateGroupClone", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
if (clones != null) {
// Process failure
for (Volume cloneObject : clones) {
cloneObject.setInactive(true);
}
dbClient.updateObject(clones);
}
_log.error("Failed to create group clone. ", e);
ServiceError serviceError = ExternalDeviceException.errors.createGroupCloneFailed("doCreateGroupClone", e.getMessage());
taskCompleter.error(dbClient, serviceError);
} finally {
try {
if (task == null || isTaskInTerminalState(task.getStatus())) {
// post process storage pool capacity for clone's pools
// map clones to their storage pool
Map<URI, List<URI>> dbPoolToClone = new HashMap<>();
for (Volume clone : clones) {
URI dbPoolUri = clone.getPool();
List<URI> poolClones = dbPoolToClone.get(dbPoolUri);
if (poolClones == null) {
poolClones = new ArrayList<>();
dbPoolToClone.put(dbPoolUri, poolClones);
}
poolClones.add(clone.getId());
}
for (URI dbPoolUri : dbPoolToClone.keySet()) {
StoragePool dbPool = dbClient.queryObject(StoragePool.class, dbPoolUri);
updateStoragePoolCapacity(dbPool, storageSystem,
dbPoolToClone.get(dbPoolUri), dbClient);
}
}
} catch (Exception ex) {
_log.error("Failed to update storage pool after create group clone operation completion.", ex);
}
}
}
@Override
public void doDetachClone(StorageSystem storageSystem, URI cloneVolume,
TaskCompleter taskCompleter) {
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = null;
Volume clone = dbClient.queryObject(Volume.class, cloneVolume);
_log.info("Detaching volume clone on storage system {}, clone: {} .",
storageSystem.getNativeId(), clone.toString());
try {
BlockObject sourceVolume = BlockObject.fetch(dbClient, clone.getAssociatedSourceVolume());
VolumeClone driverClone = new VolumeClone();
driverClone.setStorageSystemId(storageSystem.getNativeId());
driverClone.setNativeId(clone.getNativeId());
driverClone.setParentId(sourceVolume.getNativeId());
driverClone.setConsistencyGroup(clone.getReplicationGroupInstance());
// Call driver
task = driver.detachVolumeClone(Collections.unmodifiableList(Collections.singletonList(driverClone)));
// todo: need to implement support for async case.
if (task.getStatus() == DriverTask.TaskStatus.READY) {
ReplicationUtils.removeDetachedFullCopyFromSourceFullCopiesList(clone, dbClient);
clone.setAssociatedSourceVolume(NullColumnValueGetter.getNullURI());
clone.setReplicaState(Volume.ReplicationState.DETACHED.name());
String msg = String.format("doDetachClone -- Detached volume clone: %s .", task.getMessage());
_log.info(msg);
dbClient.updateObject(clone);
taskCompleter.ready(dbClient);
} else {
String msg = String.format("Failed to detach volume clone on storage system %s, clone: %s .",
storageSystem.getNativeId(), clone.toString());
_log.error(msg);
// todo: add error
ServiceError serviceError = ExternalDeviceException.errors.detachVolumeCloneFailed("doDetachClone", msg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
String msg = String.format("Failed to detach volume clone on storage system %s, clone: %s .",
storageSystem.getNativeId(), clone.toString());
_log.error(msg, e);
ServiceError serviceError = ExternalDeviceException.errors.detachVolumeCloneFailed("doDetachClone", msg);
taskCompleter.error(dbClient, serviceError);
}
}
@Override
public void doDetachGroupClone(StorageSystem storageSystem, List<URI> cloneVolumes,
TaskCompleter taskCompleter) {
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = null;
List<Volume> clones = dbClient.queryObject(Volume.class, cloneVolumes);
_log.info("Detaching group clones on storage system {}, clone: {} .",
storageSystem.getNativeId(), clones.toString());
try {
Map<VolumeClone, Volume> driverCloneToCloneMap = new HashMap<>();
List<VolumeClone> driverClones = new ArrayList<>();
for (Volume clone : clones) {
BlockObject sourceVolume = BlockObject.fetch(dbClient, clone.getAssociatedSourceVolume());
VolumeClone driverClone = new VolumeClone();
driverClone.setStorageSystemId(storageSystem.getNativeId());
driverClone.setNativeId(clone.getNativeId());
driverClone.setParentId(sourceVolume.getNativeId());
driverClone.setConsistencyGroup(clone.getReplicationGroupInstance());
driverClones.add(driverClone);
driverCloneToCloneMap.put(driverClone, clone);
}
// Call driver
task = driver.detachVolumeClone(Collections.unmodifiableList(driverClones));
// todo: need to implement support for async case.
if (task.getStatus() == DriverTask.TaskStatus.READY) {
for (Map.Entry<VolumeClone, Volume> entry : driverCloneToCloneMap.entrySet() ) {
VolumeClone driverClone = entry.getKey();
Volume clone = entry.getValue();
ReplicationUtils.removeDetachedFullCopyFromSourceFullCopiesList(clone, dbClient);
clone.setAssociatedSourceVolume(NullColumnValueGetter.getNullURI());
clone.setReplicaState(Volume.ReplicationState.DETACHED.name());
}
String msg = String.format("doDetachGroupClone -- Detached group clone: %s .", task.getMessage());
_log.info(msg);
dbClient.updateObject(clones);
taskCompleter.ready(dbClient);
} else {
String msg = String.format("Failed to detach group clone on storage system %s, clones: %s .",
storageSystem.getNativeId(), clones.toString());
_log.error(msg);
// todo: add error
ServiceError serviceError = ExternalDeviceException.errors.detachVolumeCloneFailed("doDetachGroupClone", msg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
String msg = String.format("Failed to detach group clone on storage system %s, clones: %s .",
storageSystem.getNativeId(), clones.toString());
_log.error(msg, e);
// todo: add error
ServiceError serviceError = ExternalDeviceException.errors.detachVolumeCloneFailed("doDetachGroupClone", msg);
taskCompleter.error(dbClient, serviceError);
}
}
@Override
public void doRestoreFromClone(StorageSystem storageSystem, URI cloneVolume,
TaskCompleter taskCompleter) {
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = null;
Volume clone = dbClient.queryObject(Volume.class, cloneVolume);
_log.info("Restore from volume clone on storage system {}, clone: {} .",
storageSystem.getNativeId(), clone.toString());
try {
BlockObject sourceVolume = BlockObject.fetch(dbClient, clone.getAssociatedSourceVolume());
VolumeClone driverClone = new VolumeClone();
driverClone.setStorageSystemId(storageSystem.getNativeId());
driverClone.setNativeId(clone.getNativeId());
driverClone.setParentId(sourceVolume.getNativeId());
driverClone.setConsistencyGroup(clone.getReplicationGroupInstance());
// Call driver
task = driver.restoreFromClone(Collections.unmodifiableList(Collections.singletonList(driverClone)));
if (!isTaskInTerminalState(task.getStatus())) {
// If the task is not in a terminal state and will be completed asynchronously
// create a job to monitor the progress of the request and update the clone
// volume replica state and call the completer as appropriate based on the result
// of the request.
RestoreFromCloneExternalDeviceJob job = new RestoreFromCloneExternalDeviceJob(
storageSystem.getId(), cloneVolume, task.getTaskId(), taskCompleter);
ControllerServiceImpl.enqueueJob(new QueueJob(job));
} else if (task.getStatus() == DriverTask.TaskStatus.READY) {
String msg = String.format("doRestoreFromClone -- Restored volume from clone: %s .", task.getMessage());
_log.info(msg);
ExternalDeviceUtils.updateRestoredClone(clone, driverClone, dbClient, true);
taskCompleter.ready(dbClient);
} else {
String msg = String.format("Failed to restore volume from clone on storage system %s, clone: %s .",
storageSystem.getNativeId(), clone.toString());
_log.error(msg);
// todo: add error
ServiceError serviceError = ExternalDeviceException.errors.restoreVolumesFromClonesFailed("doRestoreFromClone", msg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
String msg = String.format("Failed to restore volume from clone on storage system %s, clone: %s .",
storageSystem.getNativeId(), clone.toString());
_log.error(msg, e);
ServiceError serviceError = ExternalDeviceException.errors.restoreVolumesFromClonesFailed("doRestoreFromClone", msg);
taskCompleter.error(dbClient, serviceError);
}
}
@Override
public void doRestoreFromGroupClone(StorageSystem storageSystem,
List<URI> cloneVolumes, TaskCompleter taskCompleter) {
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = null;
List<Volume> clones = dbClient.queryObject(Volume.class, cloneVolumes);
_log.info("Restore from group clone on storage system {}, clones: {} .",
storageSystem.getNativeId(), clones.toString());
try {
Map<VolumeClone, Volume> driverCloneToCloneMap = new HashMap<>();
List<VolumeClone> driverClones = new ArrayList<>();
for (Volume clone : clones) {
BlockObject sourceVolume = BlockObject.fetch(dbClient, clone.getAssociatedSourceVolume());
VolumeClone driverClone = new VolumeClone();
driverClone.setStorageSystemId(storageSystem.getNativeId());
driverClone.setNativeId(clone.getNativeId());
driverClone.setParentId(sourceVolume.getNativeId());
driverClone.setConsistencyGroup(clone.getReplicationGroupInstance());
driverClones.add(driverClone);
driverCloneToCloneMap.put(driverClone, clone);
}
// Call driver
task = driver.restoreFromClone(Collections.unmodifiableList(driverClones));
if (!isTaskInTerminalState(task.getStatus())) {
// If the task is not in a terminal state and will be completed asynchronously
// create a job to monitor the progress of the request and update the clone
// volume replica state and call the completer as appropriate based on the result
// of the request.
RestoreFromGroupCloneExternalDeviceJob job = new RestoreFromGroupCloneExternalDeviceJob(
storageSystem.getId(), cloneVolumes, task.getTaskId(), taskCompleter);
ControllerServiceImpl.enqueueJob(new QueueJob(job));
} else if (task.getStatus() == DriverTask.TaskStatus.READY) {
for (Map.Entry<VolumeClone, Volume> entry : driverCloneToCloneMap.entrySet() ) {
VolumeClone driverClone = entry.getKey();
Volume clone = entry.getValue();
ExternalDeviceUtils.updateRestoredClone(clone, driverClone, dbClient, false);
}
String msg = String.format("doRestoreFromGroupClone -- Restore from group clone: %s .", task.getMessage());
_log.info(msg);
dbClient.updateObject(clones);
taskCompleter.ready(dbClient);
} else {
String msg = String.format("Failed to restore from group clone on storage system %s, clones: %s .",
storageSystem.getNativeId(), clones.toString());
_log.error(msg);
ServiceError serviceError = ExternalDeviceException.errors.restoreVolumesFromClonesFailed("doRestoreFromGroupClone", msg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
String msg = String.format("Failed to restore from group clone on storage system %s, clones: %s .",
storageSystem.getNativeId(), clones.toString());
_log.error(msg, e);
ServiceError serviceError = ExternalDeviceException.errors.restoreVolumesFromClonesFailed("doRestoreFromGroupClone", msg);
taskCompleter.error(dbClient, serviceError);
}
}
@Override
public void doCreateConsistencyGroup(StorageSystem storageSystem, URI consistencyGroup, String replicationGroupName, TaskCompleter taskCompleter)
throws DeviceControllerException {
_log.info("Creating consistency group for volumes START.....");
BlockConsistencyGroup cg = null;
try {
VolumeConsistencyGroup driverCG = new VolumeConsistencyGroup();
cg = dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroup);
driverCG.setDisplayName(cg.getLabel());
driverCG.setStorageSystemId(storageSystem.getNativeId());
// call driver
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = driver.createConsistencyGroup(driverCG);
// todo: need to implement support for async case.
if (task.getStatus() == DriverTask.TaskStatus.READY) {
cg.setNativeId(driverCG.getNativeId());
cg.addSystemConsistencyGroup(storageSystem.getId().toString(), cg.getLabel());
cg.addConsistencyGroupTypes(BlockConsistencyGroup.Types.LOCAL.name());
if (NullColumnValueGetter.isNullURI(cg.getStorageController())) {
cg.setStorageController(storageSystem.getId());
}
dbClient.updateObject(cg);
String msg = String.format("doCreateConsistencyGroup -- Created consistency group: %s .", task.getMessage());
_log.info(msg);
taskCompleter.ready(dbClient);
} else {
cg.setInactive(true);
dbClient.updateObject(cg);
String errorMsg = String.format("doCreateConsistencyGroup -- Failed to create Consistency Group: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.createConsistencyGroupFailed("doCreateConsistencyGroup", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
if (cg != null) {
cg.setInactive(true);
dbClient.updateObject(cg);
}
String errorMsg = String.format("doCreateConsistencyGroup -- Failed to create Consistency Group: %s .", e.getMessage());
_log.error(errorMsg, e);
ServiceError serviceError = ExternalDeviceException.errors.createConsistencyGroupFailed("doCreateConsistencyGroup", errorMsg);
taskCompleter.error(dbClient, serviceError);
} finally {
_log.info("Creating consistency group for volumes END.....");
}
}
@Override
public void doDeleteConsistencyGroup(StorageSystem storageSystem,
URI consistencyGroupId, String replicationGroupName,
Boolean keepRGName, Boolean markInactive, TaskCompleter taskCompleter)
throws DeviceControllerException {
_log.info("Delete consistency group: STARTED...");
BlockConsistencyGroup consistencyGroup = null;
String groupNativeId = null;
String groupDisplayName = null;
boolean isDeleteForBlockCG = true;
try {
if (!NullColumnValueGetter.isNullURI(consistencyGroupId)) {
consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId);
groupDisplayName = consistencyGroup != null ? consistencyGroup.getLabel() : replicationGroupName;
groupNativeId = consistencyGroup != null ? consistencyGroup.getNativeId() : replicationGroupName;
if (consistencyGroup == null) {
isDeleteForBlockCG = false;
}
} else {
groupDisplayName = replicationGroupName;
groupNativeId = replicationGroupName;
isDeleteForBlockCG = false;
}
if (groupNativeId == null || groupNativeId.isEmpty()) {
String msg = String.format("doDeleteConsistencyGroup -- There is no consistency group or replication group to delete.");
_log.info(msg);
taskCompleter.ready(dbClient);
return;
}
if (isDeleteForBlockCG) {
_log.info("Deleting consistency group: storage system {}, group {}", storageSystem.getNativeId(), groupDisplayName );
} else {
_log.info("Deleting system replication group: storage system {}, group {}", storageSystem.getNativeId(), groupDisplayName );
_log.info("Replication groups are not supported for external devices. Do not call driver." );
taskCompleter.ready(dbClient);
return;
}
// prepare driver consistency group
VolumeConsistencyGroup driverCG = new VolumeConsistencyGroup();
driverCG.setDisplayName(groupDisplayName);
driverCG.setNativeId(groupNativeId);
driverCG.setStorageSystemId(storageSystem.getNativeId());
// call driver
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = driver.deleteConsistencyGroup(driverCG);
// todo: need to implement support for async case.
if (task.getStatus() == DriverTask.TaskStatus.READY) {
if (consistencyGroup != null) {
// I followed xtremio pattern to implement this logic.
consistencyGroup.removeSystemConsistencyGroup(URIUtil.asString(storageSystem.getId()), groupDisplayName);
dbClient.updateObject(consistencyGroup);
// have to read again to get updated systemConsistencyGroup map
consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId);
/*
* Verify if the BlockConsistencyGroup references any LOCAL arrays.
* If we no longer have any references we can remove the 'LOCAL' type from the BlockConsistencyGroup.
*/
List<URI> referencedArrays = BlockConsistencyGroupUtils.getLocalSystems(consistencyGroup, dbClient);
boolean cgReferenced = referencedArrays != null && !referencedArrays.isEmpty();
if (!cgReferenced) {
// Remove the LOCAL type
StringSet cgTypes = consistencyGroup.getTypes();
cgTypes.remove(BlockConsistencyGroup.Types.LOCAL.name());
consistencyGroup.setTypes(cgTypes);
// Remove the referenced storage system as well, but only if there are no other types
// of storage systems associated with the CG.
if (!BlockConsistencyGroupUtils.referencesNonLocalCgs(consistencyGroup, dbClient)) {
consistencyGroup.setStorageController(NullColumnValueGetter.getNullURI());
// Update the consistency group model
consistencyGroup.setInactive(markInactive);
}
} else {
_log.info("*** Referenced arrays {}", referencedArrays.toString());
}
dbClient.updateObject(consistencyGroup);
}
String msg = String.format("doDeleteConsistencyGroup -- Delete consistency group: %s .", task.getMessage());
_log.info(msg);
taskCompleter.ready(dbClient);
} else {
String errorMsg = String.format("doDeleteConsistencyGroup -- Failed to delete Consistency Group: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.deleteConsistencyGroupFailed("doDeleteConsistencyGroup", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
} catch (Exception e) {
String errorMsg = String.format("doDeleteConsistencyGroup -- Failed to delete Consistency Group: %s .", e.getMessage());
_log.error(errorMsg, e);
ServiceError serviceError = ExternalDeviceException.errors.deleteConsistencyGroupFailed("doDeleteConsistencyGroup", errorMsg);
taskCompleter.error(dbClient, serviceError);
} finally {
_log.info("Delete consistency group: END...");
}
}
@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());
}
@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());
}
@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());
}
@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());
}
@Override
public void doExportRemoveVolumes(StorageSystem storage,
ExportMask exportMask, List<URI> volumes,
List<Initiator> initiators,
TaskCompleter taskCompleter) throws DeviceControllerException {
_log.info("{} doExportRemoveVolumes START ...", storage.getSerialNumber());
exportMaskOperationsHelper.removeVolumes(storage, exportMask.getId(), volumes,
initiators, taskCompleter);
_log.info("{} doExportRemoveVolumes END ...", storage.getSerialNumber());
}
@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());
}
@Override
public void doExportAddInitiators(StorageSystem storage,
ExportMask exportMask, List<URI> volumeURIs,
List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter)
throws DeviceControllerException {
_log.info("{} doExportAddInitiators START ...", storage.getSerialNumber());
exportMaskOperationsHelper.addInitiators(storage, exportMask.getId(), volumeURIs, initiators, targets, taskCompleter);
_log.info("{} doExportAddInitiators END ...", storage.getSerialNumber());
}
@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());
}
@Override
public void doExportRemoveInitiators(StorageSystem storage,
ExportMask exportMask, List<URI> volumes,
List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter)
throws DeviceControllerException {
_log.info("{} doExportRemoveInitiators START ...", storage.getSerialNumber());
exportMaskOperationsHelper.removeInitiators(storage, exportMask.getId(), volumes, initiators, targets, taskCompleter);
_log.info("{} doExportRemoveInitiators END ...", storage.getSerialNumber());
}
@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());
}
@Override
public ExportMask refreshExportMask(StorageSystem storage, ExportMask mask) throws DeviceControllerException {
return exportMaskOperationsHelper.refreshExportMask(storage, mask);
}
@Override
public void doConnect(StorageSystem storageSystem) {
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
if (driver == null) {
throw DeviceControllerException.exceptions.connectStorageFailedNoDevice(
storageSystem.getSystemType());
}
_log.info("doConnect to external device {} - start", storageSystem.getId());
_log.info("doConnect to external device {} - end", storageSystem.getId());
}
private void updateVolumesWithDriverVolumeInfo(DbClient dbClient, Map<StorageVolume, Volume> driverVolumesMap, Set<URI> consistencyGroups)
throws IOException {
for (Map.Entry driverVolumeToVolume : driverVolumesMap.entrySet()) {
StorageVolume driverVolume = (StorageVolume)driverVolumeToVolume.getKey();
Volume volume = (Volume)driverVolumeToVolume.getValue();
if (driverVolume.getNativeId() != null && driverVolume.getNativeId().length() > 0) {
volume.setNativeId(driverVolume.getNativeId());
volume.setDeviceLabel(driverVolume.getDeviceLabel());
volume.setNativeGuid(NativeGUIDGenerator.generateNativeGuid(dbClient, volume));
if (driverVolume.getWwn() == null) {
volume.setWWN(String.format("%s%s", driverVolume.getStorageSystemId(), driverVolume.getNativeId()));
} else {
volume.setWWN(driverVolume.getWwn());
}
volume.setProvisionedCapacity(driverVolume.getProvisionedCapacity());
volume.setAllocatedCapacity(driverVolume.getAllocatedCapacity());
if (!NullColumnValueGetter.isNullURI(volume.getConsistencyGroup())) {
consistencyGroups.add(volume.getConsistencyGroup());
}
} else {
volume.setInactive(true);
}
}
}
private void createVolumeSnapshots(StorageSystem storageSystem, List<BlockSnapshot> snapshots, Boolean createInactive, Boolean readOnly,
TaskCompleter taskCompleter) {
_log.info("Creating snapshots for volumes.....");
List<VolumeSnapshot> driverSnapshots = new ArrayList<>();
Map<VolumeSnapshot, BlockSnapshot> driverSnapshotToSnapshotMap = new HashMap<>();
// Prepare driver snapshots
String storageSystemNativeId = storageSystem.getNativeId();
for (BlockSnapshot snapshot : snapshots) {
Volume parent = dbClient.queryObject(Volume.class, snapshot.getParent().getURI());
VolumeSnapshot driverSnapshot = new VolumeSnapshot();
driverSnapshot.setParentId(parent.getNativeId());
driverSnapshot.setStorageSystemId(storageSystemNativeId);
driverSnapshot.setDisplayName(snapshot.getLabel());
if (readOnly) {
driverSnapshot.setAccessStatus(StorageObject.AccessStatus.READ_ONLY);
} else {
driverSnapshot.setAccessStatus(StorageObject.AccessStatus.READ_WRITE);
}
driverSnapshotToSnapshotMap.put(driverSnapshot, snapshot);
driverSnapshots.add(driverSnapshot);
}
// call driver
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = driver.createVolumeSnapshot(Collections.unmodifiableList(driverSnapshots), null);
// todo: need to implement support for async case.
if (task.getStatus() == DriverTask.TaskStatus.READY) {
// update snapshots
for (VolumeSnapshot driverSnapshot : driverSnapshotToSnapshotMap.keySet()) {
BlockSnapshot snapshot = driverSnapshotToSnapshotMap.get(driverSnapshot);
snapshot.setNativeId(driverSnapshot.getNativeId());
snapshot.setDeviceLabel(driverSnapshot.getDeviceLabel());
snapshot.setNativeGuid(NativeGUIDGenerator.generateNativeGuid(storageSystem, snapshot));
snapshot.setIsSyncActive(true);
snapshot.setReplicationGroupInstance(driverSnapshot.getConsistencyGroup());
if (driverSnapshot.getProvisionedCapacity() > 0) {
snapshot.setProvisionedCapacity(driverSnapshot.getProvisionedCapacity());
}
if (driverSnapshot.getAllocatedCapacity() > 0) {
snapshot.setAllocatedCapacity(driverSnapshot.getAllocatedCapacity());
}
}
dbClient.updateObject(driverSnapshotToSnapshotMap.values());
String msg = String.format("createVolumeSnapshots -- Created snapshots: %s .", task.getMessage());
_log.info(msg);
taskCompleter.ready(dbClient);
} else {
for (BlockSnapshot snapshot : snapshots) {
snapshot.setInactive(true);
}
dbClient.updateObject(snapshots);
String errorMsg = String.format("doCreateSnapshot -- Failed to create snapshots: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.createSnapshotsFailed("doCreateSnapshot", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
}
private void createGroupSnapshots(StorageSystem storageSystem, List<BlockSnapshot> snapshots, Boolean createInactive, Boolean readOnly,
TaskCompleter taskCompleter) {
_log.info("Creating snapshot of consistency group .....");
List<VolumeSnapshot> driverSnapshots = new ArrayList<>();
Map<VolumeSnapshot, BlockSnapshot> driverSnapshotToSnapshotMap = new HashMap<>();
URI cgUri = snapshots.get(0).getConsistencyGroup();
BlockConsistencyGroup consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, cgUri);
// Prepare driver snapshots
String storageSystemNativeId = storageSystem.getNativeId();
for (BlockSnapshot snapshot : snapshots) {
Volume parent = dbClient.queryObject(Volume.class, snapshot.getParent().getURI());
VolumeSnapshot driverSnapshot = new VolumeSnapshot();
driverSnapshot.setParentId(parent.getNativeId());
driverSnapshot.setStorageSystemId(storageSystemNativeId);
driverSnapshot.setDisplayName(snapshot.getLabel());
if (readOnly) {
driverSnapshot.setAccessStatus(StorageObject.AccessStatus.READ_ONLY);
} else {
driverSnapshot.setAccessStatus(StorageObject.AccessStatus.READ_WRITE);
}
driverSnapshotToSnapshotMap.put(driverSnapshot, snapshot);
driverSnapshots.add(driverSnapshot);
}
// Prepare driver consistency group of the parent volume
VolumeConsistencyGroup driverCG = new VolumeConsistencyGroup();
driverCG.setNativeId(consistencyGroup.getNativeId());
driverCG.setDisplayName(consistencyGroup.getLabel());
driverCG.setStorageSystemId(storageSystem.getNativeId());
// call driver
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = driver.createConsistencyGroupSnapshot(driverCG, Collections.unmodifiableList(driverSnapshots), null);
// todo: need to implement support for async case.
if (task.getStatus() == DriverTask.TaskStatus.READY) {
// update snapshots
for (VolumeSnapshot driverSnapshot : driverSnapshotToSnapshotMap.keySet()) {
BlockSnapshot snapshot = driverSnapshotToSnapshotMap.get(driverSnapshot);
snapshot.setNativeId(driverSnapshot.getNativeId());
snapshot.setDeviceLabel(driverSnapshot.getDeviceLabel());
snapshot.setNativeGuid(NativeGUIDGenerator.generateNativeGuid(storageSystem, snapshot));
snapshot.setIsSyncActive(true);
// we use driver snapshot consistency group id as replication group label for group snapshots
snapshot.setReplicationGroupInstance(driverSnapshot.getConsistencyGroup());
if (driverSnapshot.getProvisionedCapacity() > 0) {
snapshot.setProvisionedCapacity(driverSnapshot.getProvisionedCapacity());
}
if (driverSnapshot.getAllocatedCapacity() > 0) {
snapshot.setAllocatedCapacity(driverSnapshot.getAllocatedCapacity());
}
}
dbClient.updateObject(driverSnapshotToSnapshotMap.values());
String msg = String.format("createGroupSnapshots -- Created snapshots: %s .", task.getMessage());
_log.info(msg);
taskCompleter.ready(dbClient);
} else {
for (BlockSnapshot snapshot : snapshots) {
snapshot.setInactive(true);
}
dbClient.updateObject(snapshots);
String errorMsg = String.format("doCreateSnapshot -- Failed to create snapshots: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.createSnapshotsFailed("doCreateSnapshot", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
}
private void deleteVolumeSnapshot(StorageSystem storageSystem, URI snapshot,
TaskCompleter taskCompleter) {
BlockSnapshot blockSnapshot = dbClient.queryObject(BlockSnapshot.class, snapshot);
if (blockSnapshot != null && !blockSnapshot.getInactive() &&
// If the blockSnapshot.nativeId is not filled in than the
// snapshot create may have failed somehow, so we'll allow
// this case to be marked as success, so that the inactive
// state against the BlockSnapshot object can be set.
!Strings.isNullOrEmpty(blockSnapshot.getNativeId())) {
_log.info("Deleting snapshot of a volume. Snapshot: {}", snapshot);
Volume parent = dbClient.queryObject(Volume.class, blockSnapshot.getParent().getURI());
VolumeSnapshot driverSnapshot = new VolumeSnapshot();
driverSnapshot.setStorageSystemId(storageSystem.getNativeId());
driverSnapshot.setNativeId(blockSnapshot.getNativeId());
driverSnapshot.setParentId(parent.getNativeId());
driverSnapshot.setConsistencyGroup(blockSnapshot.getReplicationGroupInstance());
// call driver
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = driver.deleteVolumeSnapshot(driverSnapshot);
// todo: need to implement support for async case.
if (task.getStatus() == DriverTask.TaskStatus.READY) {
// update snapshots
blockSnapshot.setInactive(true);
dbClient.updateObject(blockSnapshot);
String msg = String.format("deleteVolumeSnapshot -- Deleted snapshot: %s .", task.getMessage());
_log.info(msg);
taskCompleter.ready(dbClient);
} else {
String errorMsg = String.format("doDeleteSnapshot -- Failed to delete snapshot: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.deleteSnapshotFailed("doDeleteSnapshot", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
} else if (blockSnapshot != null) {
blockSnapshot.setInactive(true);
dbClient.updateObject(blockSnapshot);
String msg = String.format("deleteVolumeSnapshot -- Deleted snapshot: %s .", blockSnapshot.getId());
_log.info(msg);
taskCompleter.ready(dbClient);
}
}
private void deleteGroupSnapshots(StorageSystem storageSystem, List<BlockSnapshot> groupSnapshots,
TaskCompleter taskCompleter) {
_log.info("Deleting snapshot of consistency group. Snapshots: "+Joiner.on("\t").join(groupSnapshots));
URI cgUri = groupSnapshots.get(0).getConsistencyGroup();
BlockConsistencyGroup consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, cgUri);
List<VolumeSnapshot> driverSnapshots = new ArrayList<>();
for (BlockSnapshot blockSnapshot : groupSnapshots) {
VolumeSnapshot driverSnapshot = new VolumeSnapshot();
driverSnapshot.setStorageSystemId(storageSystem.getNativeId());
driverSnapshot.setNativeId(blockSnapshot.getNativeId());
driverSnapshot.setConsistencyGroup(blockSnapshot.getReplicationGroupInstance());
Volume parent = dbClient.queryObject(Volume.class, blockSnapshot.getParent().getURI());
driverSnapshot.setParentId(parent.getNativeId());
driverSnapshots.add(driverSnapshot);
}
// call driver
BlockStorageDriver driver = getDriver(storageSystem.getSystemType());
DriverTask task = driver.deleteConsistencyGroupSnapshot(Collections.unmodifiableList(driverSnapshots));
// todo: need to implement support for async case.
if (task.getStatus() == DriverTask.TaskStatus.READY) {
// update snapshots
for (BlockSnapshot blockSnapshot : groupSnapshots) {
blockSnapshot.setInactive(true);
}
dbClient.updateObject(groupSnapshots);
String msg = String.format("deleteGroupSnapshots -- Deleted group snapshot: %s .", task.getMessage());
_log.info(msg);
taskCompleter.ready(dbClient);
} else {
String errorMsg = String.format("doDeleteSnapshot -- Failed to delete group snapshot: %s .", task.getMessage());
_log.error(errorMsg);
ServiceError serviceError = ExternalDeviceException.errors.deleteGroupSnapshotFailed("doDeleteSnapshot", errorMsg);
taskCompleter.error(dbClient, serviceError);
}
}
private void updateConsistencyGroupsWithStorageSystem(Set<URI> consistencyGroups, StorageSystem storageSystem) {
List<BlockConsistencyGroup> updateCGs = new ArrayList<>();
Iterator<BlockConsistencyGroup> consistencyGroupIterator =
dbClient.queryIterativeObjects(BlockConsistencyGroup.class, consistencyGroups, true);
while (consistencyGroupIterator.hasNext()) {
BlockConsistencyGroup consistencyGroup = consistencyGroupIterator.next();
consistencyGroup.setStorageController(storageSystem.getId());
consistencyGroup.addConsistencyGroupTypes(BlockConsistencyGroup.Types.LOCAL.name());
consistencyGroup.addSystemConsistencyGroup(storageSystem.getId().toString(), consistencyGroup.getLabel());
updateCGs.add(consistencyGroup);
}
dbClient.updateObject(updateCGs);
}
private Volume getSnapshotParentVolume(BlockSnapshot snapshot) {
Volume sourceVolume = null;
URI sourceVolURI = snapshot.getParent().getURI();
if (!NullColumnValueGetter.isNullURI(sourceVolURI)) {
sourceVolume = dbClient.queryObject(Volume.class, sourceVolURI);
}
return sourceVolume;
}
@Override
public Map<String, Set<URI>> findExportMasks(StorageSystem storage,
List<String> initiatorNames, boolean mustHaveAllPorts) throws DeviceControllerException {
return exportMaskOperationsHelper.findExportMasks(storage, initiatorNames, mustHaveAllPorts);
}
@Override
public void doWaitForSynchronized(Class<? extends BlockObject> clazz, StorageSystem storageObj, URI target, TaskCompleter completer) {
_log.info("No support for wait for synchronization for external devices.");
completer.ready(dbClient);
}
@Override
public void doWaitForGroupSynchronized(StorageSystem storageObj, List<URI> target, TaskCompleter completer)
{
_log.info("No support for wait for synchronization for external devices.");
completer.ready(dbClient);
}
/**
* Method determines if the passed task status indicates that the task is completed
* and is in a terminal state.
*
* Terminal states are:
* READY
* FAILED
* PARTIALLY_FAILED
* WARNING
* ABORTED
*
* Non-Terminal states are:
* QUEUED
* PROVISIONING
*
* @param taskStatus A reference to the task status
*
* @return true if the state is terminal, false otherwise.
*/
public boolean isTaskInTerminalState(DriverTask.TaskStatus taskStatus) {
if (DriverTask.TaskStatus.PROVISIONING == taskStatus || DriverTask.TaskStatus.QUEUED == taskStatus) {
return false;
} else {
return true;
}
}
/**
* Static method for getting an initialized block storage driver.
*
* @param driverType The driver system type.
*
* @return A reference to the initialized block storage driver.
*/
public static synchronized BlockStorageDriver getBlockStorageDriver(String driverType) {
return blockDrivers.get(driverType);
}
@Override
public boolean validateStorageProviderConnection(String ipAddress, Integer portNumber) {
// call driver to validate provider connection
boolean isConnectionValid = false;
try {
StringBuffer providerID = new StringBuffer(ipAddress).append(
HDSConstants.HYPHEN_OPERATOR).append(portNumber);
_log.info("Request to validate connection to provider, ID: {}", providerID);
URIQueryResultList providerUriList = new URIQueryResultList();
dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getStorageProviderByProviderIDConstraint(providerID.toString()),
providerUriList);
if (providerUriList.iterator().hasNext()) {
StorageProvider storageProvider = dbClient.queryObject(StorageProvider.class,
providerUriList.iterator().next());
isConnectionValid = validateStorageProviderConnection(storageProvider);
} else {
String msg = String.format("Cannot find provider with ID: %s ", providerID);
}
} catch (Exception ex) {
_log.error(
"Problem in checking provider live connection with IP address and port: {}:{} due to: ",
ipAddress, portNumber, ex);
}
return isConnectionValid;
}
public boolean validateStorageProviderConnection(StorageProvider storageProvider) {
boolean isConnectionValid = false;
try {
// call driver to validate provider connection
// get driver for the provider
BlockStorageDriver driver = getDriver(storageProvider.getInterfaceType());
String username = storageProvider.getUserName();
String password = storageProvider.getPassword();
String hostName = storageProvider.getIPAddress();
Integer providerPortNumber = storageProvider.getPortNumber();
String providerType = storageProvider.getInterfaceType();
Boolean useSsl = storageProvider.getUseSSL();
String msg = String.format("Storage provider info: type: %s, host: %s, port: %s, user: %s, useSsl: %s",
providerType, hostName, providerPortNumber, username, useSsl);
_log.info(msg);
com.emc.storageos.storagedriver.model.StorageProvider driverProvider =
new com.emc.storageos.storagedriver.model.StorageProvider();
// initialize driver provider
driverProvider.setProviderHost(hostName);
driverProvider.setPortNumber(providerPortNumber);
driverProvider.setUsername(username);
driverProvider.setPassword(password);
driverProvider.setUseSSL(useSsl);
driverProvider.setProviderType(providerType);
isConnectionValid = driver.validateStorageProviderConnection(driverProvider);
} catch (Exception ex) {
_log.error("Problem in checking connection of provider {} due to: ", storageProvider.getLabel(), ex);
}
return isConnectionValid;
}
/**
* Update storage pool capacity to the most recent values from driver.
* Release reserved capacity in the pool for set of reservedObjects.
*
* @param dbPool storage pool to update capacity
* @param dbSystem storage system where the pool is located
* @param reservedObjects list of reserved object (volumes/clones/mirrors)
* @param dbClient db client
*/
public static void updateStoragePoolCapacity(StoragePool dbPool, StorageSystem dbSystem,
List<URI> reservedObjects, DbClient dbClient) {
_log.info(String.format("Update storage pool capacity for pool %s, system %s ", dbPool.getId(),
dbSystem.getId()));
BlockStorageDriver driver = getBlockStorageDriver(dbSystem.getSystemType());
// refresh the pool
dbPool = dbClient.queryObject(StoragePool.class, dbPool.getId());
// rediscover driver storage pool
com.emc.storageos.storagedriver.model.StoragePool driverPool = driver.getStorageObject(dbSystem.getNativeId(),
dbPool.getNativeId(), com.emc.storageos.storagedriver.model.StoragePool.class);
// update pool capacity in db
if (driverPool != null) {
_log.info(String.format("Driver pool %s info: free capacity %s, subscribed capacity %s ", driverPool.getNativeId(),
driverPool.getFreeCapacity(), driverPool.getSubscribedCapacity()));
dbPool.setFreeCapacity(driverPool.getFreeCapacity());
dbPool.setSubscribedCapacity(driverPool.getSubscribedCapacity());
} else {
_log.error("Driver pool for storage pool {} and storage system {} is null.", dbPool.getNativeId(), dbSystem.getNativeId());
}
// release reserved capacity
dbPool.removeReservedCapacityForVolumes(URIUtil.asStrings(reservedObjects));
dbClient.updateObject(dbPool);
}
/**
* Check if block object has exports on device
*
* @param driver storage driver
* @param driverBlockObject driver block object
* @return true/false
*/
private boolean hasExports(BlockStorageDriver driver, StorageBlockObject driverBlockObject) {
Map<String, HostExportInfo> blocObjectToHostExportInfo = null;
// get HostExportInfo data for this block object from the driver
if (driverBlockObject instanceof VolumeClone) {
VolumeClone driverClone = (VolumeClone)driverBlockObject;
blocObjectToHostExportInfo = driver.getCloneExportInfoForHosts(driverClone);
_log.info("Export info for clone {} is {}:", driverClone, blocObjectToHostExportInfo);
} else if (driverBlockObject instanceof VolumeSnapshot) {
VolumeSnapshot driverSnapshot = (VolumeSnapshot) driverBlockObject;
blocObjectToHostExportInfo = driver.getSnapshotExportInfoForHosts(driverSnapshot);
_log.info("Export info for snapshot {} is {}:", driverSnapshot, blocObjectToHostExportInfo);
} else if (driverBlockObject instanceof StorageVolume) {
StorageVolume driverVolume = (StorageVolume)driverBlockObject;
blocObjectToHostExportInfo = driver.getVolumeExportInfoForHosts(driverVolume);
_log.info("Export info for volume {} is {}:", driverVolume, blocObjectToHostExportInfo);
} else {
// not supported type in this method
String errorMsg = String.format("Method is not supported for %s objects.", driverBlockObject.getClass().getSimpleName());
throw new RuntimeException(errorMsg);
}
return !(blocObjectToHostExportInfo == null || blocObjectToHostExportInfo.isEmpty());
}
}