use of com.emc.storageos.volumecontroller.TaskCompleter in project coprhd-controller by CoprHD.
the class SRDFOperations method rollbackSRDFMirror.
private void rollbackSRDFMirror(StorageSystem system, final Volume source, final Volume target, boolean isGrouprollback, boolean isVpoolChange, final SRDFMirrorRollbackCompleter completer) {
log.info("START Rolling back SRDF mirror");
try {
/*
* In order to help the user with any potential cleanup, use this anonymous task completer to update the
* rollback completer on whether or not a detach was successful for this source/target pair.
*/
TaskCompleter inspect = new TaskCompleter() {
@Override
protected void complete(DbClient dbClient, Operation.Status status, ServiceCoded coded) throws DeviceControllerException {
completer.addRollbackStatus(source, target, status, coded);
}
};
performDetach(system, target, isGrouprollback, inspect);
if (target.hasConsistencyGroup()) {
log.info("Removing Volume from device Group on roll back");
removeDeviceGroups(system, source.getId(), target.getId(), isVpoolChange, null);
}
} catch (Exception e) {
String msg = format(FAILURE_MSG_FMT, "rollback", source.getId(), target.getId());
log.warn(msg, e);
// Re-throw in order to cleanup any CG and RDFG.
throw e;
}
}
use of com.emc.storageos.volumecontroller.TaskCompleter in project coprhd-controller by CoprHD.
the class SmisCommandHelper method waitForAsyncSmisJob.
private void waitForAsyncSmisJob(StorageSystem storageDevice, CIMObjectPath cimJobPath, SmisJob job) throws SmisException {
if (job == null) {
TaskCompleter taskCompleter = new TaskCompleter() {
@Override
public void ready(DbClient dbClient) throws DeviceControllerException {
}
@Override
public void error(DbClient dbClient, ServiceCoded serviceCoded) throws DeviceControllerException {
}
@Override
protected void complete(DbClient dbClient, Operation.Status status, ServiceCoded coded) throws DeviceControllerException {
}
};
job = new SmisJob(cimJobPath, storageDevice.getId(), taskCompleter, "");
} else {
job.setCimJob(cimJobPath);
}
JobContext jobContext = new JobContext(_dbClient, _cimConnection, null, null, null, null, this);
long startTime = System.currentTimeMillis();
int sync_wrapper_time_out = InvokeTestFailure.internalOnlyOverrideSyncWrapperTimeOut(SYNC_WRAPPER_TIME_OUT);
while (true) {
JobPollResult result = job.poll(jobContext, SYNC_WRAPPER_WAIT);
if (!result.isJobInTerminalState()) {
if (System.currentTimeMillis() - startTime > sync_wrapper_time_out) {
throw new SmisException("Timed out waiting on smis job to complete after " + (System.currentTimeMillis() - startTime) + " milliseconds");
} else {
try {
Thread.sleep(SYNC_WRAPPER_WAIT);
} catch (InterruptedException e) {
_log.error("Thread waiting for smis job to complete was interrupted and " + "will be resumed");
}
}
} else {
if (result.isJobInTerminalFailedState()) {
throw new SmisException("Smis job failed: " + result.getErrorDescription());
}
break;
}
}
}
use of com.emc.storageos.volumecontroller.TaskCompleter in project coprhd-controller by CoprHD.
the class VPlexDeviceController method addStepsForRemoveInitiators.
/**
* Add steps necessary for Export removeInitiators.
* This routine may be called multiple times for an Export Group on each
* exportMask that needs to be adjusted.
*
* @param vplex
* -- StorageSystem VPLEX
* @param workflow
* -- Workflow steps being added to
* @param exportGroup
* -- ExportGroup
* @param exportMask
* -- ExportMask being processed
* @param initiators
* -- List<Initiator> initiators being removed
* @param hostURI
* -- Host URI
* @param hostInitiatorURIs
* -- list of Host Initiators
* @param errorMessages
* -- collector for any validation-related error messages
* @param previousStep
* -- previous step to wait on
* @return String last step added to workflow; null if no steps added
* @throws Exception
*/
private String addStepsForRemoveInitiators(StorageSystem vplex, Workflow workflow, ExportRemoveInitiatorCompleter completer, ExportGroup exportGroup, ExportMask exportMask, List<Initiator> initiators, URI hostURI, List<URI> initiatorsAlreadyRemovedFromExportGroup, StringBuffer errorMessages, String previousStep) throws Exception {
String lastStep = previousStep;
// assemble a list of other ExportGroups that reference this ExportMask
List<ExportGroup> otherExportGroups = ExportUtils.getOtherExportGroups(exportGroup, exportMask, _dbClient);
_log.info(String.format("will be removing initiators %s for host %s mask %s (%s)", getInitiatorsWwnsString(initiators), hostURI.toString(), exportMask.getMaskName(), exportMask.getId()));
List<URI> hostInitiatorURIs = new ArrayList<URI>();
List<URI> removeInitiatorListURIs = new ArrayList<URI>();
for (Initiator initiator : initiators) {
// contained within this ExportMask (CTRL-12300)
if (exportMask.hasInitiator(initiator.getId().toString())) {
hostInitiatorURIs.add(initiator.getId());
}
// removeInitiatorListURIs is created to pass it to next method name addStepsForInitiatorRemoval
// we have filtering logic placed inside addStepsForInitiatorRemoval to take care of it.
removeInitiatorListURIs.add(initiator.getId());
}
// Determine the targets we should remove for the initiators being removed.
// Normally one or more targets will be removed for each initiator that
// is removed according to the zoning map.
List<URI> targetURIs = getTargetURIs(exportMask, hostInitiatorURIs);
_log.info(String.format("will be removing targets %s for host %s", targetURIs.toString(), hostURI.toString()));
// What is about to happen...
//
// 1. if all the initiators in the storage view are getting removed
// and there are no existing (external) initiators in the Export Mask
// then delete the storage view
//
// 2. if there are other ExportGroups referencing this ExportMask
// AND
// if there are no more of this ExportMask's initiators present
// in this ExportGroup's initiators
// THEN
// remove this ExportGroup's volumes
// that aren't also present in other ExportGroups
// from this ExportMask
// AND FINALLY
// remove the initiator(s) from the ExportGroup
// but NOT from the ExportMask
// (because other ExportGroups are referencing it still)
//
// 3. otherwise,
// just remove initiators from the storage view, and if removing
// all initiators, also remove any volumes present in the ExportGroup
boolean removeAllInits = (hostInitiatorURIs.size() >= exportMask.getInitiators().size());
boolean canDeleteMask = removeAllInits && !exportMask.hasAnyExistingInitiators();
if (canDeleteMask) {
if (!ExportUtils.exportMaskHasBothExclusiveAndSharedVolumes(exportGroup, otherExportGroups, exportMask)) {
_log.info("all initiators are being removed and no " + "other ExportGroups reference ExportMask {}", exportMask.getMaskName());
_log.info("creating a deleteStorageView workflow step for " + exportMask.getMaskName());
String exportMaskDeleteStep = workflow.createStepId();
Workflow.Method storageViewExecuteMethod = deleteStorageViewMethod(vplex.getId(), exportGroup.getId(), exportMask.getId(), false);
lastStep = workflow.createStep(DELETE_STORAGE_VIEW, String.format("Delete VPLEX Storage View %s for ExportGroup %s", exportMask.getMaskName(), exportGroup.getId()), lastStep, vplex.getId(), vplex.getSystemType(), this.getClass(), storageViewExecuteMethod, null, exportMaskDeleteStep);
// Add zoning step for deleting ExportMask
List<NetworkZoningParam> zoningParam = NetworkZoningParam.convertExportMasksToNetworkZoningParam(exportGroup.getId(), Collections.singletonList(exportMask.getId()), _dbClient);
Workflow.Method zoneMaskDeleteMethod = _networkDeviceController.zoneExportMasksDeleteMethod(zoningParam, StringSetUtil.stringSetToUriList(exportMask.getVolumes().keySet()));
Workflow.Method zoneNullRollbackMethod = _networkDeviceController.zoneNullRollbackMethod();
lastStep = workflow.createStep(null, "Zone delete mask: " + exportMask.getMaskName(), lastStep, nullURI, "network-system", _networkDeviceController.getClass(), zoneMaskDeleteMethod, zoneNullRollbackMethod, null);
} else {
// export Mask has shared and exclusive volumes, removing the volumes.
// The volumes can be taken from the export Group
List<URI> volumeURIList = URIUtil.toURIList(exportGroup.getVolumes().keySet());
if (!volumeURIList.isEmpty()) {
List<Volume> volumes = _dbClient.queryObject(Volume.class, volumeURIList);
String volumesForDisplay = Joiner.on(", ").join(Collections2.transform(volumes, CommonTransformerFunctions.fctnDataObjectToForDisplay()));
_log.info("there are some volumes that need to be removed: " + volumesForDisplay);
_log.info("creating a remove volumes workflow step with " + exportMask.getMaskName() + " for volumes " + CommonTransformerFunctions.collectionToString(volumeURIList));
completer.addExportMaskToRemovedVolumeMapping(exportMask.getId(), volumeURIList);
TaskCompleter taskCompleter = new ExportMaskRemoveVolumeCompleter(exportGroup.getId(), exportMask.getId(), volumeURIList, lastStep);
Workflow.Method storageViewRemoveVolume = storageViewRemoveVolumesMethod(vplex.getId(), exportGroup.getId(), exportMask.getId(), volumeURIList, lastStep, taskCompleter, null);
lastStep = workflow.createStep("removeVolumes", String.format("Removing volumes from export on storage array %s (%s) for export mask %s (%s)", vplex.getNativeGuid(), vplex.getId().toString(), exportMask.getMaskName(), exportMask.getId()), lastStep, vplex.getId(), vplex.getSystemType(), this.getClass(), storageViewRemoveVolume, rollbackMethodNullMethod(), null);
// Add zoning step for removing volumes
List<NetworkZoningParam> zoningParam = NetworkZoningParam.convertExportMasksToNetworkZoningParam(exportGroup.getId(), Collections.singletonList(exportMask.getId()), _dbClient);
Workflow.Method zoneRemoveVolumesMethod = _networkDeviceController.zoneExportRemoveVolumesMethod(zoningParam, volumeURIList);
lastStep = workflow.createStep(null, "Zone remove volumes mask: " + exportMask.getMaskName(), lastStep, nullURI, "network-system", _networkDeviceController.getClass(), zoneRemoveVolumesMethod, _networkDeviceController.zoneNullRollbackMethod(), null);
}
}
} else {
// this is just a simple initiator removal, so just do it...
lastStep = addStepsForInitiatorRemoval(vplex, workflow, completer, exportGroup, exportMask, removeInitiatorListURIs, targetURIs, lastStep, removeAllInits, hostURI, errorMessages);
}
return lastStep;
}
use of com.emc.storageos.volumecontroller.TaskCompleter 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);
}
}
use of com.emc.storageos.volumecontroller.TaskCompleter in project coprhd-controller by CoprHD.
the class VPlexDeviceController method createFullCopy.
/**
* {@inheritDoc}
*/
@Override
public void createFullCopy(URI vplexURI, List<VolumeDescriptor> volumeDescriptors, String opId) throws ControllerException {
_log.info("Copy volumes on VPLEX", vplexURI);
// When we copy a VPLEX virtual volume we natively copy the primary backend
// volume of the virtual volume. This primary copy is then imported to the
// VPLEX as a local or distributed virtual volume depending upon the type of
// the VPLEX volume being copied. Note that we could be creating multiple
// copies of a single source volume that is not in a consistency group or
// creating a single copy of multiple source volumes which are in a consistency
// group.
URI vplexSrcVolumeURI = null;
List<URI> vplexCopyVolumesURIs = new ArrayList<URI>();
TaskCompleter completer = null;
try {
// Set up the task completer
List<VolumeDescriptor> vplexVolumeDescrs = VolumeDescriptor.filterByType(volumeDescriptors, new VolumeDescriptor.Type[] { Type.VPLEX_VIRT_VOLUME }, new VolumeDescriptor.Type[] {});
List<VolumeDescriptor> vplexSrcVolumeDescs = getDescriptorsForFullCopySrcVolumes(vplexVolumeDescrs);
// VPLEX copy volumes
vplexVolumeDescrs.removeAll(vplexSrcVolumeDescs);
vplexCopyVolumesURIs = VolumeDescriptor.getVolumeURIs(vplexVolumeDescrs);
completer = new VPlexTaskCompleter(Volume.class, vplexCopyVolumesURIs, opId, null);
Volume firstFullCopy = _dbClient.queryObject(Volume.class, vplexCopyVolumesURIs.get(0));
URI sourceVolume = firstFullCopy.getAssociatedSourceVolume();
Volume source = URIUtil.isType(sourceVolume, Volume.class) ? _dbClient.queryObject(Volume.class, sourceVolume) : null;
VolumeGroup volumeGroup = (source != null) ? source.getApplication(_dbClient) : null;
if (volumeGroup != null && !ControllerUtils.checkVolumeForVolumeGroupPartialRequest(_dbClient, source)) {
_log.info("Creating full copy for Application {}", volumeGroup.getLabel());
// add VolumeGroup to task completer
completer.addVolumeGroupId(volumeGroup.getId());
}
// Generate the Workflow.
Workflow workflow = _workflowService.getNewWorkflow(this, COPY_VOLUMES_WF_NAME, false, opId);
_log.info("Created new full copy workflow with operation id {}", opId);
/**
* Volume descriptors contains
* 1. VPLEX v-volume to be copied (source),
* 2. VPLEX copy v-volume to be created,
* 3. backend source volume to be created as clone for (1)'s backend volume
* 4. Empty volume to be created as HA backend volume for (2) in case of Distributed
*
* Group the given volume descriptors by backend Array Replication Group (RG).
* -For each RG entry, create workflow steps,
* -These RG steps run in parallel
*/
Map<String, List<VolumeDescriptor>> repGroupToVolumeDescriptors = groupDescriptorsByReplicationGroup(volumeDescriptors);
for (String repGroupName : repGroupToVolumeDescriptors.keySet()) {
_log.info("Processing Array Replication Group {}", repGroupName);
List<URI> vplexCopyVolumeURIs = new ArrayList<URI>();
List<VolumeDescriptor> volumeDescriptorsRG = repGroupToVolumeDescriptors.get(repGroupName);
// We need to know which of the passed volume descriptors represents
// the VPLEX virtual volumes that are being copied. We also remove it
// from the list, so the only VPLEX volumes in the list are those
// of the copies.
List<VolumeDescriptor> vplexVolumeDescriptors = VolumeDescriptor.filterByType(volumeDescriptorsRG, new VolumeDescriptor.Type[] { Type.VPLEX_VIRT_VOLUME }, new VolumeDescriptor.Type[] {});
List<VolumeDescriptor> vplexSrcVolumeDescrs = getDescriptorsForFullCopySrcVolumes(vplexVolumeDescriptors);
vplexVolumeDescriptors.removeAll(vplexSrcVolumeDescrs);
_log.info("Got volume descriptors for VPLEX volumes being copied.");
// Get the URIs of the VPLEX copy volumes.
vplexCopyVolumeURIs.addAll(VolumeDescriptor.getVolumeURIs(vplexVolumeDescriptors));
vplexURI = getDataObject(Volume.class, vplexCopyVolumeURIs.get(0), _dbClient).getStorageController();
// Add a rollback step to make sure all full copies are
// marked inactive and that the full copy relationships
// are updated. This will also mark any HA backend volumes
// inactive in the case the copies are distributed. The
// step only provides functionality on rollback. Normal
// execution is a no-op.
List<URI> volumesToMarkInactive = new ArrayList<URI>();
volumesToMarkInactive.addAll(vplexCopyVolumeURIs);
List<VolumeDescriptor> blockDescriptors = VolumeDescriptor.filterByType(volumeDescriptorsRG, new VolumeDescriptor.Type[] { Type.BLOCK_DATA }, new VolumeDescriptor.Type[] {});
if (!blockDescriptors.isEmpty()) {
volumesToMarkInactive.addAll(VolumeDescriptor.getVolumeURIs(blockDescriptors));
}
StorageSystem vplexSystem = getDataObject(StorageSystem.class, vplexURI, _dbClient);
Workflow.Method executeMethod = rollbackMethodNullMethod();
Workflow.Method rollbackMethod = markVolumesInactiveMethod(volumesToMarkInactive);
String waitFor = workflow.createStep(null, "Mark volumes inactive on rollback", null, vplexURI, vplexSystem.getSystemType(), this.getClass(), executeMethod, rollbackMethod, null);
if (!vplexSrcVolumeDescrs.isEmpty()) {
// Find the backend volume that is the primary volume for one of
// the VPLEX volumes being copied. The primary backend volume is the
// associated volume in the same virtual array as the VPLEX volume.
// It does not matter which one if there are multiple source VPLEX
// volumes. These volumes will all be in a consistency group and
// use the same backend storage system.
VolumeDescriptor vplexSrcVolumeDescr = vplexSrcVolumeDescrs.get(0);
vplexSrcVolumeURI = vplexSrcVolumeDescr.getVolumeURI();
BlockObject primarySourceObject = getPrimaryForFullCopySrcVolume(vplexSrcVolumeURI);
_log.info("Primary volume/snapshot is {}", primarySourceObject.getId());
// add CG to taskCompleter
if (!NullColumnValueGetter.isNullURI(primarySourceObject.getConsistencyGroup())) {
completer.addConsistencyGroupId(primarySourceObject.getConsistencyGroup());
}
}
// Next, create a step to create and start an import volume
// workflow for each copy.
createStepsForFullCopyImport(workflow, vplexURI, vplexVolumeDescriptors, volumeDescriptorsRG, waitFor);
_log.info("Created workflow steps to import the primary copies");
}
_log.info("Executing workflow plan");
FullCopyOperationCompleteCallback wfCompleteCB = new FullCopyOperationCompleteCallback();
workflow.executePlan(completer, String.format("Copy of VPLEX volume %s completed successfully", vplexSrcVolumeURI), wfCompleteCB, new Object[] { vplexCopyVolumesURIs }, null, null);
_log.info("Workflow plan executing");
} catch (Exception e) {
String failMsg = String.format("Copy of VPLEX volume %s failed", vplexSrcVolumeURI);
_log.error(failMsg, e);
ServiceError serviceError = VPlexApiException.errors.fullCopyVolumesFailed((vplexSrcVolumeURI != null ? vplexSrcVolumeURI.toString() : ""), e);
failStep(completer, opId, serviceError);
}
}
Aggregations