use of com.emc.storageos.svcs.errorhandling.resources.InternalException in project coprhd-controller by CoprHD.
the class VPlexDeviceController method addStepsForChangeVirtualPool.
@Override
public String addStepsForChangeVirtualPool(Workflow workflow, String waitFor, List<VolumeDescriptor> volumes, String taskId) throws InternalException {
try {
// Get all the Virtual Volumes.
List<VolumeDescriptor> vplexVirtualVolumes = VolumeDescriptor.filterByType(volumes, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.VPLEX_VIRT_VOLUME }, new VolumeDescriptor.Type[] {});
if (vplexVirtualVolumes == null || vplexVirtualVolumes.isEmpty()) {
return waitFor;
}
// Find the original change vpool virtual volumes from the descriptors.
URI newVpoolURI = null;
List<URI> changeVpoolVirtualVolumeURIs = new ArrayList<URI>();
for (VolumeDescriptor vplexVirtualVolume : vplexVirtualVolumes) {
if (vplexVirtualVolume.getParameters() != null && !vplexVirtualVolume.getParameters().isEmpty()) {
// Let's check to see if the PARAM_VPOOL_CHANGE_EXISTING_VOLUME_ID was populated
// in the descriptor params map. This would indicate that the descriptor
// has information about the existing volume for the change vpool operation.
Object existingVolumeId = vplexVirtualVolume.getParameters().get(VolumeDescriptor.PARAM_VPOOL_CHANGE_EXISTING_VOLUME_ID);
if (existingVolumeId != null) {
URI virtualVolumeURI = (URI) existingVolumeId;
_log.info(String.format("Adding steps for change vpool for vplex volume %s", virtualVolumeURI.toString()));
if (newVpoolURI == null) {
newVpoolURI = (URI) vplexVirtualVolume.getParameters().get(VolumeDescriptor.PARAM_VPOOL_CHANGE_NEW_VPOOL_ID);
}
changeVpoolVirtualVolumeURIs.add(virtualVolumeURI);
}
}
}
// Check to see if this is an RP+VPLEX change vpool request
List<VolumeDescriptor> rpExistingSourceVolumes = VolumeDescriptor.filterByType(volumes, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.RP_EXISTING_SOURCE }, new VolumeDescriptor.Type[] {});
// Check to see if this is an RP+VPLEX change vpool request
List<VolumeDescriptor> rpExistingProtectedSourceVolumes = VolumeDescriptor.filterByType(volumes, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.RP_EXISTING_PROTECTED_SOURCE }, new VolumeDescriptor.Type[] {});
boolean rpAddProtectionVPoolChange = (rpExistingSourceVolumes != null && !rpExistingSourceVolumes.isEmpty());
boolean rpUpgradeProtectionVPoolChange = (rpExistingProtectedSourceVolumes != null && !rpExistingProtectedSourceVolumes.isEmpty());
// RP+VPLEX change vpool request
if (rpAddProtectionVPoolChange || rpUpgradeProtectionVPoolChange) {
// First let's make a copy of the all the volume descriptors passed in
List<VolumeDescriptor> copyOfVolumeDescriptors = new ArrayList<VolumeDescriptor>();
copyOfVolumeDescriptors.addAll(volumes);
if (rpAddProtectionVPoolChange) {
_log.info("Adding VPLEX steps for RP+VPLEX/MetroPoint add protection vpool change...");
Iterator<VolumeDescriptor> it = copyOfVolumeDescriptors.iterator();
while (it.hasNext()) {
VolumeDescriptor currentVolumeDesc = it.next();
if (changeVpoolVirtualVolumeURIs.contains(currentVolumeDesc.getVolumeURI())) {
// Remove the RP+VPLEX Source Change Vpool Virtual Volume(s) from the copy of the
// descriptors as they do not need to be created (because they already exist!)
it.remove();
break;
}
}
// already)
for (URI virtualVolumeURI : changeVpoolVirtualVolumeURIs) {
Volume changeVpoolVolume = getDataObject(Volume.class, virtualVolumeURI, _dbClient);
changeVpoolVolume.getConsistencyGroup();
// This is a good time to update the vpool on the existing Virtual Volume to the new vpool
changeVpoolVolume.setVirtualPool(newVpoolURI);
_dbClient.updateObject(changeVpoolVolume);
StorageSystem vplex = getDataObject(StorageSystem.class, changeVpoolVolume.getStorageController(), _dbClient);
// Get a handle on the RP consistency group manager
ConsistencyGroupManager consistencyGroupManager = getConsistencyGroupManager(DiscoveredDataObject.Type.rp.name());
// Add step for create CG
waitFor = consistencyGroupManager.addStepsForCreateConsistencyGroup(workflow, waitFor, vplex, Arrays.asList(virtualVolumeURI), false);
_log.info("Added steps for CG creation for vplex volume {}", virtualVolumeURI);
}
} else {
_log.info("Adding VPLEX steps for RP+VPLEX/MetroPoint upgrade protection vpool change...");
}
// Let's now create the virtual volumes for the RP+VPLEX:
// Source Journal, Target(s), and Target Journal.
waitFor = addStepsForCreateVolumes(workflow, waitFor, copyOfVolumeDescriptors, taskId);
}
// Create steps to migrate the backend volumes.
String lastStep = waitFor;
URI cgURI = null;
// With application support, one VPLEX CG could have multiple replication groups from the same local system.
// The localSystemToRemoveCG map key is storagesystemUri, value is the list of replication group names to be removed.
Map<URI, Set<String>> localSystemsToRemoveCG = new HashMap<URI, Set<String>>();
List<VolumeDescriptor> vplexMigrateVolumes = VolumeDescriptor.filterByType(volumes, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.VPLEX_MIGRATE_VOLUME }, new VolumeDescriptor.Type[] {});
if (vplexMigrateVolumes != null && !vplexMigrateVolumes.isEmpty()) {
for (URI virtualVolumeURI : changeVpoolVirtualVolumeURIs) {
_log.info("Adding migration steps for vplex volume {}", virtualVolumeURI);
// A list of the volumes satisfying the new VirtualPool to
// which the data on the current backend volumes
// will be migrated.
List<URI> newVolumes = new ArrayList<URI>();
// A Map containing a migration for each new backend
// volume
Map<URI, URI> migrationMap = new HashMap<URI, URI>();
// A map that specifies the storage pool in which
// each new volume should be created.
Map<URI, URI> poolVolumeMap = new HashMap<URI, URI>();
// The URI of the vplex system
URI vplexURI = null;
for (VolumeDescriptor desc : vplexMigrateVolumes) {
// Skip migration targets that are not for the VPLEX
// volume being processed.
Migration migration = getDataObject(Migration.class, desc.getMigrationId(), _dbClient);
if (!migration.getVolume().equals(virtualVolumeURI)) {
continue;
}
// migrated when multiple volumes are passed.
if (vplexURI == null) {
Volume virtualVolume = getDataObject(Volume.class, virtualVolumeURI, _dbClient);
vplexURI = virtualVolume.getStorageController();
cgURI = virtualVolume.getConsistencyGroup();
}
// Set data required to add the migration steps.
newVolumes.add(desc.getVolumeURI());
migrationMap.put(desc.getVolumeURI(), desc.getMigrationId());
poolVolumeMap.put(desc.getVolumeURI(), desc.getPoolURI());
// If the migration is to a different storage system
// we may need to remove the backend CG on the source
// system after the migration completes. Note that the
// migration source is null for an ingested volume
// being migrated to known storage.
Volume migSrc = null;
URI migSrcURI = migration.getSource();
if (!NullColumnValueGetter.isNullURI(migSrcURI)) {
migSrc = getDataObject(Volume.class, migSrcURI, _dbClient);
}
URI migTgtURI = migration.getTarget();
Volume migTgt = getDataObject(Volume.class, migTgtURI, _dbClient);
if ((migSrc != null) && (!migTgt.getStorageController().equals(migSrc.getStorageController()))) {
// If we have a volume to migrate and the RG field is NOT set on the volume,
// do not remove the RG on the local system.
//
// Volumes that are in RGs that are being migrated are grouped together so otherwise
// we're good as the replication instance will be set on those volumes.
String rgName = migSrc.getReplicationGroupInstance();
if (NullColumnValueGetter.isNotNullValue(rgName)) {
URI storageUri = migSrc.getStorageController();
Set<String> rgNames = localSystemsToRemoveCG.get(storageUri);
if (rgNames == null) {
rgNames = new HashSet<String>();
localSystemsToRemoveCG.put(storageUri, rgNames);
}
rgNames.add(rgName);
_log.info("Will remove CG {} on local system {}", rgName, storageUri);
} else {
_log.info("Will not remove CG on local system {}", migSrc.getStorageController());
}
}
}
// Note that the last step here is a step group associated
// with deleting the migration sources after the migrations
// have completed and committed. This means that anything
// that waits on this, will occur after the migrations have
// completed, been committed, and the migration sources deleted.
lastStep = addStepsForMigrateVolumes(workflow, vplexURI, virtualVolumeURI, newVolumes, migrationMap, poolVolumeMap, newVpoolURI, null, VolumeDescriptor.getMigrationSuspendBeforeCommit(volumes), VolumeDescriptor.getMigrationSuspendBeforeDeleteSource(volumes), taskId, waitFor);
_log.info("Add migration steps for vplex volume {}", virtualVolumeURI);
}
// systemConsistencyGroup specified for the group.
if (!NullColumnValueGetter.isNullURI(cgURI)) {
_log.info("Vpool change volumes are in CG {}", cgURI);
BlockConsistencyGroup cg = getDataObject(BlockConsistencyGroup.class, cgURI, _dbClient);
if (cg.checkForType(Types.LOCAL)) {
_log.info("CG {} has local type", cgURI);
// If any of the VPLEX volumes involved in the vpool change
// is in a VPLEX CG with corresponding local CGs for the backend
// volumes, then it is required that all VPLEX volumes in the
// CG are part of the vpool change. If the backend volumes are being
// migrated to a new storage system, then we need to add a step
// to delete the local CG.
boolean localCGDeleted = false;
for (Map.Entry<URI, Set<String>> entry : localSystemsToRemoveCG.entrySet()) {
localCGDeleted = true;
URI localSystemURI = entry.getKey();
StorageSystem localSystem = getDataObject(StorageSystem.class, localSystemURI, _dbClient);
Set<String> rgNames = entry.getValue();
for (String rgName : rgNames) {
_log.info("Adding step to remove CG {} on local system {}", rgName, localSystemURI);
Workflow.Method deleteCGMethod = new Workflow.Method("deleteReplicationGroupInConsistencyGroup", localSystemURI, cgURI, rgName, false, false, true);
workflow.createStep("deleteLocalCG", String.format("Delete consistency group from storage system: %s", localSystemURI), lastStep, localSystemURI, localSystem.getSystemType(), BlockDeviceController.class, deleteCGMethod, null, null);
}
}
if (localCGDeleted) {
lastStep = "deleteLocalCG";
}
}
}
}
// Return the last step
return lastStep;
} catch (Exception ex) {
throw VPlexApiException.exceptions.addStepsForChangeVirtualPoolFailed(ex);
}
}
use of com.emc.storageos.svcs.errorhandling.resources.InternalException in project coprhd-controller by CoprHD.
the class VPlexDeviceController method resyncSnapshot.
/**
* {@inheritDoc}
*/
@Override
public void resyncSnapshot(URI vplexURI, URI snapshotURI, String opId) throws InternalException {
// The snapshot target volume could be the source side backend volume for
// a VPLEX volume if a VPLEX volume was created on the snapshot target volume
// for the purpose of exporting the snapshot through the VPLEX rather directly
// through the backend storage system. If this is the case, and that snapshot
// is resynchronized, then we need do some additional steps because the data
// on the VPLEX backend volume will have changed, and the VPLEX volume needs
// to know about that.
BlockSnapshot snapshot = getDataObject(BlockSnapshot.class, snapshotURI, _dbClient);
try {
// Create a new the Workflow.
Workflow workflow = _workflowService.getNewWorkflow(this, RESYNC_SNAPSHOT_WF_NAME, false, opId);
_log.info("Created resync snapshot workflow with operation id {}", opId);
// Get all snapshots that will be resync'd.
List<BlockSnapshot> snapshotsToResync = new ArrayList<BlockSnapshot>();
URI cgURI = snapshot.getConsistencyGroup();
if (!NullColumnValueGetter.isNullURI(cgURI)) {
snapshotsToResync = ControllerUtils.getSnapshotsPartOfReplicationGroup(snapshot, _dbClient);
} else {
snapshotsToResync.add(snapshot);
}
// Get a list of the VPLEX volumes, if any, that are built
// using the snapshot target volume.
List<Volume> vplexVolumes = VPlexUtil.getVPlexVolumesBuiltOnSnapshots(snapshotsToResync, _dbClient);
// Create the workflow steps.
if (vplexVolumes.isEmpty()) {
// If there are no VPLEX volumes built on the snapshots to be resynchronized,
// then we just need a single step to invoke the block device controller to
// resync the snapshots.
createWorkflowStepForResyncNativeSnapshot(workflow, snapshot, null, null);
} else {
// Maps Vplex volume that needs to be flushed to underlying array volume
Map<Volume, Volume> vplexToArrayVolumesToFlush = new HashMap<Volume, Volume>();
for (Volume vplexVolume : vplexVolumes) {
Volume arrayVolumeToBeResynced = VPlexUtil.getVPLEXBackendVolume(vplexVolume, true, _dbClient);
vplexToArrayVolumesToFlush.put(vplexVolume, arrayVolumeToBeResynced);
}
Map<URI, String> vplexVolumeIdToDetachStep = new HashMap<URI, String>();
String waitFor = null;
// Generate pre restore steps
waitFor = addPreRestoreResyncSteps(workflow, vplexToArrayVolumesToFlush, vplexVolumeIdToDetachStep, waitFor);
// Now create a workflow step to natively resync the snapshot.
// Note that if the snapshot is associated with a CG, then block
// controller will resync all snapshots in the snapshot set. We
// execute this after the invalidate cache.
waitFor = createWorkflowStepForResyncNativeSnapshot(workflow, snapshot, waitFor, rollbackMethodNullMethod());
// Generate post restore steps
waitFor = addPostRestoreResyncSteps(workflow, vplexToArrayVolumesToFlush, vplexVolumeIdToDetachStep, waitFor);
}
// Execute the workflow.
_log.info("Executing workflow plan");
TaskCompleter completer = new BlockSnapshotResyncCompleter(snapshot, opId);
String successMsg = String.format("Resynchronize VPLEX native snapshot %s from volume %s " + "completed successfully", snapshotURI, snapshot.getParent().getURI());
workflow.executePlan(completer, successMsg);
_log.info("Workflow plan executing");
} catch (Exception e) {
String failMsg = String.format("Resynchronize VPLEX native snapshot %s failed", snapshotURI);
_log.error(failMsg, e);
TaskCompleter completer = new BlockSnapshotResyncCompleter(snapshot, opId);
ServiceError serviceError = VPlexApiException.errors.restoreVolumeFailed(snapshotURI.toString(), e);
failStep(completer, opId, serviceError);
}
}
use of com.emc.storageos.svcs.errorhandling.resources.InternalException in project coprhd-controller by CoprHD.
the class VPlexDeviceController method addStepsForMigrateVolumes.
/**
* Adds steps in the passed workflow to migrate a volume.
*
* @param workflow
* @param vplexURI
* @param virtualVolumeURI
* @param targetVolumeURIs
* @param migrationsMap
* @param poolVolumeMap
* @param newVpoolURI
* @param newVarrayURI
* @param suspendBeforeCommit
* @param suspendBeforeDeleteSource
* @param opId
* @param waitFor
* @return
* @throws InternalException
*/
public String addStepsForMigrateVolumes(Workflow workflow, URI vplexURI, URI virtualVolumeURI, List<URI> targetVolumeURIs, Map<URI, URI> migrationsMap, Map<URI, URI> poolVolumeMap, URI newVpoolURI, URI newVarrayURI, boolean suspendBeforeCommit, boolean suspendBeforeDeleteSource, String opId, String waitFor) throws InternalException {
try {
_log.info("VPlex controller migrate volume {} on VPlex {}", virtualVolumeURI, vplexURI);
String volumeUserLabel = "Label Unknown";
Volume virtualVolume = getDataObject(Volume.class, virtualVolumeURI, _dbClient);
if (virtualVolume != null && virtualVolume.getDeviceLabel() != null && virtualVolume.getLabel() != null) {
volumeUserLabel = virtualVolume.getLabel() + " (" + virtualVolume.getDeviceLabel() + ")";
}
// Get the VPlex storage system
StorageSystem vplexSystem = getDataObject(StorageSystem.class, vplexURI, _dbClient);
_log.info("Got VPlex system");
// Create a step to validate the volume and prevent migration if the
// the ViPR DB does not properly reflect the actual backend volumes.
// A successful migration will delete the backend source volumes. If
// the ViPR DB does not correctly reflect the actual backend volume,
// we could delete a backend volume used by some other VPLEX volume.
waitFor = createWorkflowStepToValidateVPlexVolume(workflow, vplexSystem, virtualVolumeURI, waitFor);
Map<URI, Volume> volumeMap = new HashMap<URI, Volume>();
Map<URI, StorageSystem> storageSystemMap = new HashMap<URI, StorageSystem>();
for (URI volumeURI : targetVolumeURIs) {
Volume volume = getDataObject(Volume.class, volumeURI, _dbClient);
volumeMap.put(volumeURI, volume);
StorageSystem storageSystem = getDataObject(StorageSystem.class, volume.getStorageController(), _dbClient);
storageSystemMap.put(volume.getStorageController(), storageSystem);
}
// Set the project and tenant.
Volume firstVolume = volumeMap.values().iterator().next();
Project vplexProject = VPlexUtil.lookupVplexProject(firstVolume, vplexSystem, _dbClient);
URI tenantURI = vplexProject.getTenantOrg().getURI();
_log.info("Project is {}, Tenant is {}", vplexProject.getId(), tenantURI);
waitFor = createWorkflowStepsForBlockVolumeExport(workflow, vplexSystem, storageSystemMap, volumeMap, vplexProject.getId(), tenantURI, waitFor);
_log.info("Created workflow steps for volume export.");
// Now make a migration Step for each passed target to which data
// for the passed virtual volume will be migrated. The migrations
// will be done from this controller.
Iterator<URI> targetVolumeIter = targetVolumeURIs.iterator();
while (targetVolumeIter.hasNext()) {
URI targetVolumeURI = targetVolumeIter.next();
_log.info("Target volume is {}", targetVolumeURI);
URI migrationURI = migrationsMap.get(targetVolumeURI);
_log.info("Migration is {}", migrationURI);
String stepId = workflow.createStepId();
_log.info("Migration opId is {}", stepId);
Workflow.Method vplexExecuteMethod = new Workflow.Method(MIGRATE_VIRTUAL_VOLUME_METHOD_NAME, vplexURI, virtualVolumeURI, targetVolumeURI, migrationURI, newVarrayURI);
Workflow.Method vplexRollbackMethod = new Workflow.Method(RB_MIGRATE_VIRTUAL_VOLUME_METHOD_NAME, vplexURI, migrationURI, stepId);
_log.info("Creating workflow migration step");
workflow.createStep(MIGRATION_CREATE_STEP, String.format("VPlex %s migrating to target volume %s.", vplexSystem.getId().toString(), targetVolumeURI.toString()), waitFor, vplexSystem.getId(), vplexSystem.getSystemType(), getClass(), vplexExecuteMethod, vplexRollbackMethod, stepId);
_log.info("Created workflow migration step");
}
// Once the migrations complete, we will commit the migrations.
// So, now we create the steps to commit the migrations.
String waitForStep = MIGRATION_CREATE_STEP;
List<URI> migrationURIs = new ArrayList<URI>(migrationsMap.values());
List<URI> migrationSources = new ArrayList<URI>();
Iterator<URI> migrationsIter = migrationsMap.values().iterator();
while (migrationsIter.hasNext()) {
URI migrationURI = migrationsIter.next();
_log.info("Migration is {}", migrationURI);
Migration migration = getDataObject(Migration.class, migrationURI, _dbClient);
// The migration source volume may be null for ingested volumes
// for which we do not know anything about the backend volumes.
// If we don't know the source, we know we are migrating an
// ingested volume and we will not want to do any renaming
// after the commit as we do when migration ViPR create volumes,
// which adhere to a standard naming convention.
Boolean rename = Boolean.TRUE;
if (migration.getSource() != null) {
migrationSources.add(migration.getSource());
} else {
rename = Boolean.FALSE;
}
_log.info("Added migration source {}", migration.getSource());
String stepId = workflow.createStepId();
_log.info("Commit operation id is {}", stepId);
Workflow.Method vplexExecuteMethod = new Workflow.Method(COMMIT_MIGRATION_METHOD_NAME, vplexURI, virtualVolumeURI, migrationURI, rename, newVpoolURI, newVarrayURI);
Workflow.Method vplexRollbackMethod = new Workflow.Method(RB_COMMIT_MIGRATION_METHOD_NAME, migrationURIs, newVpoolURI, newVarrayURI, stepId);
_log.info("Creating workflow step to commit migration");
String stepDescription = String.format("migration commit step on VPLEX %s of volume %s", vplexSystem.getSerialNumber(), volumeUserLabel);
waitForStep = workflow.createStep(MIGRATION_COMMIT_STEP, stepDescription, waitForStep, vplexSystem.getId(), vplexSystem.getSystemType(), getClass(), vplexExecuteMethod, vplexRollbackMethod, suspendBeforeCommit, stepId);
workflow.setSuspendedStepMessage(stepId, COMMIT_MIGRATION_SUSPEND_MESSAGE);
_log.info("Created workflow step to commit migration");
}
// Create a step that creates a sub workflow to delete the old
// migration source volumes, which are no longer used by the
// virtual volume. We also update the virtual volume CoS. If
// we make it to this step, then all migrations were committed.
// We do this in a sub workflow because we don't won't to
// initiate rollback regardless of success or failure.
String stepId = workflow.createStepId();
Workflow.Method vplexExecuteMethod = new Workflow.Method(DELETE_MIGRATION_SOURCES_METHOD, vplexURI, virtualVolumeURI, newVpoolURI, newVarrayURI, migrationSources);
List<String> migrationSourceLabels = new ArrayList<>();
Iterator<Volume> volumeIter = _dbClient.queryIterativeObjects(Volume.class, migrationSources);
while (volumeIter.hasNext()) {
migrationSourceLabels.add(volumeIter.next().getNativeGuid());
}
String stepDescription = String.format("post-migration delete of original source backing volumes [%s] associated with virtual volume %s", Joiner.on(',').join(migrationSourceLabels), volumeUserLabel);
workflow.createStep(DELETE_MIGRATION_SOURCES_STEP, stepDescription, waitForStep, vplexSystem.getId(), vplexSystem.getSystemType(), getClass(), vplexExecuteMethod, null, suspendBeforeDeleteSource, stepId);
workflow.setSuspendedStepMessage(stepId, DELETE_MIGRATION_SOURCES_SUSPEND_MESSAGE);
_log.info("Created workflow step to create sub workflow for source deletion");
return DELETE_MIGRATION_SOURCES_STEP;
} catch (Exception e) {
throw VPlexApiException.exceptions.addStepsForChangeVirtualPoolFailed(e);
}
}
use of com.emc.storageos.svcs.errorhandling.resources.InternalException in project coprhd-controller by CoprHD.
the class VPlexDeviceController method deactivateMirror.
@Override
public void deactivateMirror(URI vplexURI, URI mirrorURI, List<VolumeDescriptor> volumeDescriptors, String taskId) throws InternalException {
VplexMirrorDeactivateCompleter completer = new VplexMirrorDeactivateCompleter(mirrorURI, taskId);
try {
// Generate the Workflow.
Workflow workflow = _workflowService.getNewWorkflow(this, DEACTIVATE_MIRROR_WF_NAME, true, taskId);
// the wait for key returned by previous call
String waitFor = null;
// Add steps for detaching and deleting mirror
waitFor = addStepsForDetachAndDeleteMirror(workflow, waitFor, vplexURI, mirrorURI, taskId);
// Next, call the BlockDeviceController to add its methods.
waitFor = _blockDeviceController.addStepsForDeleteVolumes(workflow, waitFor, volumeDescriptors, taskId);
String successMessage = "Deactivate mirror successful for: " + mirrorURI;
workflow.executePlan(completer, successMessage);
} catch (Exception ex) {
_log.error("Deactivate mirror failed for mirror " + mirrorURI, ex);
ServiceError serviceError = VPlexApiException.errors.deactivateMirrorFailed(ex);
failStep(completer, taskId, serviceError);
}
}
use of com.emc.storageos.svcs.errorhandling.resources.InternalException in project coprhd-controller by CoprHD.
the class VPlexDeviceController method restoreVolume.
/**
* {@inheritDoc}
*/
@Override
public void restoreVolume(URI vplexURI, URI snapshotURI, String opId) throws InternalException {
BlockSnapshot snapshot = getDataObject(BlockSnapshot.class, snapshotURI, _dbClient);
try {
// Generate the Workflow.
Workflow workflow = _workflowService.getNewWorkflow(this, RESTORE_VOLUME_WF_NAME, false, opId);
_log.info("Created restore volume workflow with operation id {}", opId);
// Get some info from the snapshot we need to do the native
// restore of the backend volume. Note that if the snapshot
// is associated with a CG, then all backend volumes in the
// CG will be restored using their corresponding snapshots,
// meaning the VPLEX volumes using those backend volumes
// will be restored.
URI parentSystemURI = snapshot.getStorageController();
StorageSystem parentSystem = getDataObject(StorageSystem.class, parentSystemURI, _dbClient);
URI parentVolumeURI = snapshot.getParent().getURI();
Volume parentVolume = getDataObject(Volume.class, parentVolumeURI, _dbClient);
URI parentPoolURI = parentVolume.getPool();
// Get the VPLEX system.
StorageSystem vplexSystem = getDataObject(StorageSystem.class, vplexURI, _dbClient);
// Get the VPLEX volume(s) to be restored.
List<URI> vplexVolumeURIs = new ArrayList<URI>();
URI cgURI = snapshot.getConsistencyGroup();
if (NullColumnValueGetter.isNullURI(cgURI)) {
// If the snapshot is not in a CG, the only VPLEX
// volume to restore is the VPLEX volume using the
// snapshot parent.
URIQueryResultList queryResults = new URIQueryResultList();
_dbClient.queryByConstraint(AlternateIdConstraint.Factory.getVolumeByAssociatedVolumesConstraint(parentVolumeURI.toString()), queryResults);
URI vplexVolumeURI = queryResults.iterator().next();
vplexVolumeURIs.add(vplexVolumeURI);
} else {
// Otherwise, get all snapshots in the snapset, get the
// parent volume for each snapshot, and get the VLPEX
// volume using the snapshot parent.
List<BlockSnapshot> cgSnaps = ControllerUtils.getSnapshotsPartOfReplicationGroup(snapshot, _dbClient);
for (BlockSnapshot cgSnapshot : cgSnaps) {
URIQueryResultList queryResults = new URIQueryResultList();
_dbClient.queryByConstraint(AlternateIdConstraint.Factory.getVolumeByAssociatedVolumesConstraint(cgSnapshot.getParent().getURI().toString()), queryResults);
URI vplexVolumeURI = queryResults.iterator().next();
vplexVolumeURIs.add(vplexVolumeURI);
}
}
// The workflow depends on if the VPLEX volumes are local
// or distributed.
String waitFor = null;
Volume firstVplexVolume = getDataObject(Volume.class, vplexVolumeURIs.get(0), _dbClient);
if (null == firstVplexVolume.getAssociatedVolumes() || firstVplexVolume.getAssociatedVolumes().isEmpty()) {
_log.error("VPLEX volume {} has no backend volumes.", firstVplexVolume.forDisplay());
throw InternalServerErrorException.internalServerErrors.noAssociatedVolumesForVPLEXVolume(firstVplexVolume.forDisplay());
}
boolean isLocal = firstVplexVolume.getAssociatedVolumes().size() == 1;
if (isLocal) {
// VPLEX volume.
for (URI vplexVolumeURI : vplexVolumeURIs) {
waitFor = createWorkflowStepForInvalidateCache(workflow, vplexSystem, vplexVolumeURI, null, null);
}
// Now create a workflow step to natively restore the backend
// volume from the passed snapshot. Note that if the snapshot
// is associated with a CG, then block controller will restore
// all backend volumes in the CG using their corresponding
// snapshots. We execute this after the invalidate cache. We
// could execute these in parallel for a little better efficiency,
// but what if the invalidate cache fails, but the restore succeeds,
// the cache now has invalid data and a cache read hit could return
// invalid data.
createWorkflowStepForRestoreNativeSnapshot(workflow, parentSystem, parentVolumeURI, snapshotURI, parentPoolURI, waitFor, null);
} else {
for (URI vplexVolumeURI : vplexVolumeURIs) {
// For distributed volumes we take snapshots of and restore the
// source backend volume. Before we can do the restore, we need
// to detach the mirror of the distributed volume. So, create a
// workflow step to detach it from the source.
Volume vplexVolume = getDataObject(Volume.class, vplexVolumeURI, _dbClient);
String detachStepId = workflow.createStepId();
Workflow.Method restoreVolumeRollbackMethod = createRestoreResyncRollbackMethod(vplexURI, vplexVolumeURI, vplexVolume.getConsistencyGroup(), detachStepId);
waitFor = createWorkflowStepForDetachMirror(workflow, vplexSystem, vplexVolume, detachStepId, null, restoreVolumeRollbackMethod);
// We now create a step to invalidate the cache for the
// VPLEX volume. Note that if this step fails we need to
// rollback and reattach the mirror.
createWorkflowStepForInvalidateCache(workflow, vplexSystem, vplexVolumeURI, waitFor, rollbackMethodNullMethod());
// Now create a workflow step to reattach the mirror to initiate
// a rebuild of the mirror for the distributed volume. Note that
// these steps will not run until after the native restore, which
// only gets executed once, not for every VPLEX volume.
createWorkflowStepForAttachMirror(workflow, vplexSystem, vplexVolume, detachStepId, RESTORE_VOLUME_STEP, rollbackMethodNullMethod());
}
// Create a workflow step to native restore the backend volume
// from the passed snapshot. This step is executed after the
// cache has been invalidated for each VPLEX volume. Note that
// if the snapshot is associated with a CG, then block controller
// will restore all backend volumes in the CG using their
// corresponding snapshots. We could execute this in parallel
// with the restore for a little better efficiency, but what if
// the invalidate cache fails, but the restore succeeds, the
// cache now has invalid data and a cache read hit could return
// invalid data. If this step fails, then again, we need to
// be sure and rollback and reattach the mirror. There is
// nothing to rollback for the cache invalidate step. It just
// means there will be no read cache hits on the volume for a
// while until the cache is repopulated.
createWorkflowStepForRestoreNativeSnapshot(workflow, parentSystem, parentVolumeURI, snapshotURI, parentPoolURI, INVALIDATE_CACHE_STEP, rollbackMethodNullMethod());
}
// Execute the workflow.
_log.info("Executing workflow plan");
TaskCompleter completer = new BlockSnapshotRestoreCompleter(snapshot, opId);
String successMsg = String.format("Restore VPLEX volume from snapshot %s of backend volume %s " + "completed successfully", snapshotURI, parentVolumeURI);
workflow.executePlan(completer, successMsg);
_log.info("Workflow plan executing");
} catch (Exception e) {
String failMsg = String.format("Restore VPLEX volume from snapshot %s failed", snapshotURI);
_log.error(failMsg, e);
TaskCompleter completer = new BlockSnapshotRestoreCompleter(snapshot, opId);
ServiceError serviceError = VPlexApiException.errors.restoreVolumeFailed(snapshotURI.toString(), e);
failStep(completer, opId, serviceError);
}
}
Aggregations