use of com.emc.storageos.workflow.WorkflowException in project coprhd-controller by CoprHD.
the class VPlexDeviceController method deleteMirrorDevice.
/**
* Do the delete of a VPlex mirror device .
* This is called as a Workflow Step.
* NOTE: The parameters here must match deleteMirrorDeviceMethod above (except stepId).
*
* @param vplexURI
* URI of the VPlex StorageSystem
* @param vplexMirrorURI
* URI of the mirror to be deleted
* @param stepId
* The stepId used for completion.
*
* @throws WorkflowException
* When an error occurs updating the workflow step
* state.
*/
public void deleteMirrorDevice(URI vplexURI, URI vplexMirrorURI, String stepId) throws WorkflowException {
try {
WorkflowStepCompleter.stepExecuting(stepId);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplexURI, _dbClient);
VplexMirror vplexMirror = getDataObject(VplexMirror.class, vplexMirrorURI, _dbClient);
if (vplexMirror.getDeviceLabel() != null) {
// Call to delete mirror device
client.deleteLocalDevice(vplexMirror.getDeviceLabel());
if (vplexMirror.getSource() != null && vplexMirror.getSource().getURI() != null) {
Volume vplexVolume = getDataObject(Volume.class, vplexMirror.getSource().getURI(), _dbClient);
StorageSystem vplexSystem = getDataObject(StorageSystem.class, vplexURI, _dbClient);
updateThinProperty(client, vplexSystem, vplexVolume);
}
// Record VPLEX mirror delete event.
recordBourneVplexMirrorEvent(vplexMirrorURI, OperationTypeEnum.DELETE_VOLUME_MIRROR.getEvType(true), Operation.Status.ready, OperationTypeEnum.DELETE_VOLUME_MIRROR.getDescription());
} else {
_log.info("It seems vplex mirror {} was never created, so just move to the next step.", vplexMirror.getLabel());
}
WorkflowStepCompleter.stepSucceded(stepId);
} catch (VPlexApiException vae) {
_log.error("Exception deleting VPlex Virtual Volume: " + vae.getMessage(), vae);
recordBourneVplexMirrorEvent(vplexMirrorURI, OperationTypeEnum.DELETE_VOLUME_MIRROR.getEvType(true), Operation.Status.error, OperationTypeEnum.DELETE_VOLUME_MIRROR.getDescription());
WorkflowStepCompleter.stepFailed(stepId, vae);
} catch (Exception ex) {
_log.error("Exception deleting VPlex Virtual Volume: " + ex.getMessage(), ex);
String opName = ResourceOperationTypeEnum.DELETE_VPLEX_LOCAL_MIRROR.getName();
ServiceError serviceError = VPlexApiException.errors.deleteMirrorFailed(opName, ex);
recordBourneVplexMirrorEvent(vplexMirrorURI, OperationTypeEnum.DELETE_VOLUME_MIRROR.getEvType(true), Operation.Status.error, OperationTypeEnum.DELETE_VOLUME_MIRROR.getDescription());
WorkflowStepCompleter.stepFailed(stepId, serviceError);
}
}
use of com.emc.storageos.workflow.WorkflowException in project coprhd-controller by CoprHD.
the class VPlexDeviceController method deleteMigrationSources.
/**
* This step is executed after a virtual volume is successfully migrated to
* delete the old source volumes. We create a sub workflow to perform this
* task. We do this in a sub workflow because we don't want any of the steps
* required to delete the sources to initiate rollback in the main workflow
* if it fails.
*
* We also update the class of service, if required, for the virtual
* volume when the migration is the result of a CoS change. When this
* step is executed we know that the migrations have been committed and
* the new CoS now applies to the virtual volume.
*
* @param vplexURI
* The URI of the VPlex storage system.
* @param virtualVolumeURI
* The URI of the virtual volume.
* @param newVpoolURI
* The CoS to be assigned to the virtual volume
* upon successful commit of the migration or null when not
* specified.
* @param newVarrayURI
* The varray to be assigned to the virtual volume
* upon successful commit of the migration or null when not
* specified.
* @param migrationSources
* The migration sources to delete.
* @param stepId
* The workflow step id.
* @throws WorkflowException
*/
public void deleteMigrationSources(URI vplexURI, URI virtualVolumeURI, URI newVpoolURI, URI newVarrayURI, List<URI> migrationSources, String stepId) throws WorkflowException {
try {
WorkflowStepCompleter.stepExecuting(stepId);
// First update the virtual volume CoS, if necessary.
Volume volume = _dbClient.queryObject(Volume.class, virtualVolumeURI);
// or create a new exportGroup
if (newVarrayURI != null && volume.isVolumeExported(_dbClient)) {
URIQueryResultList exportGroupURIs = new URIQueryResultList();
_dbClient.queryByConstraint(ContainmentConstraint.Factory.getBlockObjectExportGroupConstraint(virtualVolumeURI), exportGroupURIs);
Iterator<URI> iterator = exportGroupURIs.iterator();
while (iterator.hasNext()) {
URI egUri = iterator.next();
ExportGroup eg = _dbClient.queryObject(ExportGroup.class, egUri);
if (eg != null) {
StringMap volumesMap = eg.getVolumes();
String lun = volumesMap.get(virtualVolumeURI.toString());
if (lun == null || lun.isEmpty()) {
lun = ExportGroup.LUN_UNASSIGNED_DECIMAL_STR;
}
List<URI> initiators = StringSetUtil.stringSetToUriList(eg.getInitiators());
ExportGroup newEg = null;
if (initiators != null && !initiators.isEmpty()) {
URI initiatorUri = initiators.get(0);
AlternateIdConstraint constraint = AlternateIdConstraint.Factory.getExportGroupInitiatorConstraint(initiatorUri.toString());
URIQueryResultList egUris = new URIQueryResultList();
_dbClient.queryByConstraint(constraint, egUris);
Iterator<URI> egIt = egUris.iterator();
while (egIt.hasNext()) {
ExportGroup theEg = _dbClient.queryObject(ExportGroup.class, egIt.next());
if (theEg.getVirtualArray().equals(newVarrayURI)) {
List<URI> theEgInits = StringSetUtil.stringSetToUriList(theEg.getInitiators());
if (theEgInits.containsAll(initiators) && theEgInits.size() == initiators.size()) {
_log.info(String.format("Found existing exportGroup %s", theEg.getId().toString()));
newEg = theEg;
break;
}
}
}
}
if (newEg != null) {
// add the volume to the export group
newEg.addVolume(virtualVolumeURI, Integer.valueOf(lun));
_dbClient.updateObject(newEg);
} else {
// create a new export group
_log.info("Creating new ExportGroup");
createExportGroup(eg, volume, Integer.valueOf(lun));
}
eg.removeVolume(virtualVolumeURI);
_dbClient.updateObject(eg);
}
}
}
if (!migrationSources.isEmpty()) {
final String workflowKey = "deleteOriginalSources";
if (!WorkflowService.getInstance().hasWorkflowBeenCreated(stepId, workflowKey)) {
// Now create and execute the sub workflow to delete the
// migration source volumes if we have any. If the volume
// migrated was ingested VPLEX volume we will not have
// the sources.
String subTaskId = stepId;
Workflow subWorkflow = _workflowService.getNewWorkflow(this, DELETE_MIGRATION_SOURCES_WF_NAME, true, subTaskId);
WorkflowTaskCompleter completer = new WorkflowTaskCompleter(subWorkflow.getWorkflowURI(), subTaskId);
// Creates steps to remove the migration source volumes from all
// export groups containing them and delete them.
boolean unexportStepsAdded = vplexAddUnexportVolumeWfSteps(subWorkflow, null, migrationSources, null);
// Only need to wait for unexport if there was a step for it added
// to the workflow.
String waitFor = null;
if (unexportStepsAdded) {
waitFor = UNEXPORT_STEP;
// If the migration sources are unexported, Add a step to
// forget these backend volumes.
addStepToForgetVolumes(subWorkflow, vplexURI, migrationSources, waitFor);
}
// Add steps to delete the volumes.
Iterator<URI> migrationSourcesIter = migrationSources.iterator();
while (migrationSourcesIter.hasNext()) {
URI migrationSourceURI = migrationSourcesIter.next();
_log.info("Migration source URI is {}", migrationSourceURI);
Volume migrationSource = _dbClient.queryObject(Volume.class, migrationSourceURI);
URI sourceSystemURI = migrationSource.getStorageController();
_log.info("Source storage system URI is {}", sourceSystemURI);
StorageSystem sourceSystem = _dbClient.queryObject(StorageSystem.class, sourceSystemURI);
String subWFStepId = subWorkflow.createStepId();
Workflow.Method deleteVolumesMethod = new Workflow.Method(DELETE_VOLUMES_METHOD_NAME, sourceSystemURI, Arrays.asList(migrationSourceURI));
_log.info("Creating workflow step to delete source");
subWorkflow.createStep(MIGRATION_VOLUME_DELETE_STEP, String.format("Delete volume from storage system: %s", sourceSystemURI), waitFor, sourceSystemURI, sourceSystem.getSystemType(), BlockDeviceController.class, deleteVolumesMethod, null, subWFStepId);
_log.info("Created workflow step to delete source");
}
// Execute this sub workflow.
subWorkflow.executePlan(completer, "Deleted migration sources");
// Mark this workflow as created/executed so we don't do it again on retry/resume
WorkflowService.getInstance().markWorkflowBeenCreated(stepId, workflowKey);
}
} else {
// No sources to delete. Must have migrated an ingested volume.
WorkflowStepCompleter.stepSucceded(stepId);
_log.info("Updated workflow step to success");
}
} catch (Exception ex) {
// Log the error.
_log.error("Error deleting migration sources", ex);
// Always return success. This is a cleanup step after a
// successfully committed migration. We don't want rollback,
// so we return success.
WorkflowStepCompleter.stepSucceded(stepId);
}
}
use of com.emc.storageos.workflow.WorkflowException in project coprhd-controller by CoprHD.
the class VPlexDeviceController method rollbackCreateVirtualVolumes.
/**
* Rollback any virtual volumes previously created.
*
* @param vplexURI
* @param vplexVolumeURIs
* @param executeStepId
* - step Id of the execute step; used to retrieve rollback data.
* @param stepId
* @throws WorkflowException
*/
public void rollbackCreateVirtualVolumes(URI vplexURI, List<URI> vplexVolumeURIs, String executeStepId, String stepId) throws WorkflowException {
try {
List<List<VolumeInfo>> rollbackData = (List<List<VolumeInfo>>) _workflowService.loadStepData(executeStepId);
if (rollbackData != null) {
WorkflowStepCompleter.stepExecuting(stepId);
// Get the API client.
StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
// For each virtual volume attempted, try and rollback.
for (List<VolumeInfo> rollbackList : rollbackData) {
client.deleteVirtualVolume(rollbackList);
}
}
WorkflowStepCompleter.stepSucceded(stepId);
} catch (VPlexApiException vae) {
_log.error("Exception rollback VPlex Virtual Volume create: " + vae.getLocalizedMessage(), vae);
WorkflowStepCompleter.stepFailed(stepId, vae);
} catch (Exception ex) {
_log.error("Exception rollback VPlex Virtual Volume create: " + ex.getLocalizedMessage(), ex);
ServiceError serviceError = VPlexApiException.errors.createVirtualVolumesRollbackFailed(stepId, ex);
WorkflowStepCompleter.stepFailed(stepId, serviceError);
}
}
use of com.emc.storageos.workflow.WorkflowException in project coprhd-controller by CoprHD.
the class VPlexDeviceController method storageViewRemoveVolumes.
/**
* @param client
* -- VPlexApiClient used for communication
* @param exportGroupURI
* -- Export Group
* @param exportMaskURI
* -- ExportMask corresponding to the StorageView
* @param volumeURIList
* -- URI of virtual volumes
* @param parentStepId
* -- Workflow parent step id
* @param taskCompleter
* -- the task completer, used to find the rollback context,
* which will be non-null in the case of rollback
* @param rollbackContextKey
* context key for rollback processing
* @param stepId
* -- Workflow step id
* @throws WorkflowException
*/
public void storageViewRemoveVolumes(URI vplexURI, URI exportGroupURI, URI exportMaskURI, List<URI> volumeURIList, String parentStepId, TaskCompleter taskCompleter, String rollbackContextKey, String stepId) throws WorkflowException {
ExportMaskRemoveVolumeCompleter completer = null;
ExportGroup exportGroup = null;
ExportMask exportMask = null;
try {
WorkflowStepCompleter.stepExecuting(stepId);
List<URI> volumeIdsToProcess = new ArrayList<>(volumeURIList);
exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
completer = new ExportMaskRemoveVolumeCompleter(exportGroup.getId(), exportMask.getId(), volumeURIList, stepId);
// get the context from the task completer, in case this is a rollback.
if (taskCompleter != null && rollbackContextKey != null) {
ExportOperationContext context = (ExportOperationContext) WorkflowService.getInstance().loadStepData(rollbackContextKey);
if (context != null) {
// a non-null context means this step is running as part of a rollback.
List<URI> addedVolumes = new ArrayList<>();
if (context.getOperations() != null) {
_log.info("Handling removeVolumes as a result of rollback");
ListIterator<ExportOperationContextOperation> li = context.getOperations().listIterator(context.getOperations().size());
while (li.hasPrevious()) {
ExportOperationContextOperation operation = (ExportOperationContextOperation) li.previous();
if (operation != null && VplexExportOperationContext.OPERATION_ADD_VOLUMES_TO_STORAGE_VIEW.equals(operation.getOperation())) {
addedVolumes = (List<URI>) operation.getArgs().get(0);
_log.info("Removing volumes {} as part of rollback", Joiner.on(',').join(addedVolumes));
}
}
if (addedVolumes == null || addedVolumes.isEmpty()) {
_log.info("There was no context found for add volumes. So there is nothing to rollback.");
completer.ready(_dbClient);
return;
}
}
// change the list of initiators to process to the
// list that successfully were added during addInitiators.
volumeIdsToProcess.clear();
volumeIdsToProcess.addAll(addedVolumes);
}
}
StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
// Removes the specified volumes from the storage view
// and updates the export mask.
removeVolumesFromStorageViewAndMask(client, exportMask, volumeIdsToProcess, parentStepId);
completer.ready(_dbClient);
} catch (VPlexApiException vae) {
_log.error("Exception removing volumes from Storage View: " + vae.getMessage(), vae);
failStep(completer, stepId, vae);
} catch (Exception ex) {
_log.error("Exception removing volumes from Storage View: " + ex.getMessage(), ex);
String opName = ResourceOperationTypeEnum.REMOVE_STORAGE_VIEW_VOLUME.getName();
ServiceError serviceError = VPlexApiException.errors.storageViewRemoveVolumeFailed(exportMask != null ? exportMask.getMaskName() : "none", opName, ex);
failStep(completer, stepId, serviceError);
}
}
use of com.emc.storageos.workflow.WorkflowException in project coprhd-controller by CoprHD.
the class VPlexDeviceController method storageViewRemoveStoragePorts.
/**
* Workflow Step to remove storage ports from Storage View.
* Note arguments (except stepId) must match storageViewRemoveStoragePortsMethod above.
*
* @param vplexURI
* -- URI of VPlex StorageSystem
* @param exportURI
* -- ExportGroup URI
* @param maskURI
* -- ExportMask URI.
* @param targetURIs
* -- list of targets URIs (VPLEX FE ports) to be removed.
* If non null, the targets (VPlex front end ports) indicated by the targetURIs will be removed
* from the Storage View.
* @param rollbackContextKey
* -- Context token for rollback processing
* @param stepId
* -- Workflow step id.
* @throws WorkflowException
*/
public void storageViewRemoveStoragePorts(URI vplexURI, URI exportURI, URI maskURI, List<URI> targetURIs, String rollbackContextKey, String stepId) throws DeviceControllerException {
ExportMaskRemoveInitiatorCompleter completer = null;
try {
WorkflowStepCompleter.stepExecuting(stepId);
completer = new ExportMaskRemoveInitiatorCompleter(exportURI, maskURI, new ArrayList<URI>(), stepId);
StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
ExportMask exportMask = _dbClient.queryObject(ExportMask.class, maskURI);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
String vplexClusterName = VPlexUtil.getVplexClusterName(exportMask, vplexURI, client, _dbClient);
Map<String, String> targetPortMap = VPlexControllerUtils.getTargetPortToPwwnMap(client, vplexClusterName);
VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, exportMask.getMaskName());
_log.info("Refreshing ExportMask {}", exportMask.getMaskName());
VPlexControllerUtils.refreshExportMask(_dbClient, storageView, exportMask, targetPortMap, _networkDeviceController);
// get the context from the task completer, in case this is a rollback.
if (rollbackContextKey != null) {
ExportOperationContext context = (ExportOperationContext) WorkflowService.getInstance().loadStepData(rollbackContextKey);
if (context != null) {
// a non-null context means this step is running as part of a rollback.
List<URI> addedTargets = new ArrayList<>();
if (context.getOperations() != null) {
_log.info("Handling storageViewRemoveStoragePorts as a result of rollback");
ListIterator<ExportOperationContextOperation> li = context.getOperations().listIterator(context.getOperations().size());
while (li.hasPrevious()) {
ExportOperationContextOperation operation = (ExportOperationContextOperation) li.previous();
if (operation != null && VplexExportOperationContext.OPERATION_ADD_TARGETS_TO_STORAGE_VIEW.equals(operation.getOperation())) {
addedTargets = (List<URI>) operation.getArgs().get(0);
_log.info(String.format("Removing target port(s) %s from storage view %s as part of rollback", Joiner.on(',').join(addedTargets), exportMask.getMaskName()));
}
}
}
if (addedTargets == null || addedTargets.isEmpty()) {
_log.info("There was no context found for add target. So there is nothing to rollback.");
completer.ready(_dbClient);
return;
}
// Change the list of targets to process to the list
// that successfully were added during addStoragePorts.
targetURIs.clear();
targetURIs.addAll(addedTargets);
}
}
// validate the remove storage port operation against the export mask volumes
// this is conceptually the same as remove initiators, so will validate with volumes
List<URI> volumeURIList = (exportMask.getUserAddedVolumes() != null) ? URIUtil.toURIList(exportMask.getUserAddedVolumes().values()) : new ArrayList<URI>();
if (volumeURIList.isEmpty()) {
_log.warn("volume URI list for validating remove initiators is empty...");
}
// removing all storage ports but leaving the existing initiators and volumes.
if (!exportMask.hasAnyExistingInitiators() && !exportMask.hasAnyExistingVolumes()) {
ExportMaskValidationContext ctx = new ExportMaskValidationContext();
ctx.setStorage(vplex);
ctx.setExportMask(exportMask);
ctx.setBlockObjects(volumeURIList, _dbClient);
ctx.setAllowExceptions(!WorkflowService.getInstance().isStepInRollbackState(stepId));
validator.removeInitiators(ctx).validate();
if (targetURIs != null && targetURIs.isEmpty() == false) {
List<PortInfo> targetPortInfos = new ArrayList<PortInfo>();
List<URI> targetsToRemoveFromStorageView = new ArrayList<URI>();
for (URI target : targetURIs) {
// Do not try to remove a port twice.
if (!exportMask.getStoragePorts().contains(target.toString())) {
continue;
}
// Build the PortInfo structure for the port to be added
StoragePort port = getDataObject(StoragePort.class, target, _dbClient);
PortInfo pi = new PortInfo(port.getPortNetworkId().toUpperCase().replaceAll(":", ""), null, port.getPortName(), null);
targetPortInfos.add(pi);
targetsToRemoveFromStorageView.add(target);
}
if (!targetPortInfos.isEmpty()) {
// Remove the targets from the VPLEX
client.removeTargetsFromStorageView(exportMask.getMaskName(), targetPortInfos);
// Remove the targets to the database.
for (URI target : targetsToRemoveFromStorageView) {
exportMask.removeTarget(target);
}
_dbClient.updateObject(exportMask);
}
}
}
completer.ready(_dbClient);
} catch (VPlexApiException vae) {
_log.error("Exception removing storage ports from Storage View: " + vae.getMessage(), vae);
failStep(completer, stepId, vae);
} catch (Exception ex) {
_log.error("Exception removing storage ports from Storage View: " + ex.getMessage(), ex);
String opName = ResourceOperationTypeEnum.DELETE_STORAGE_VIEW_STORAGEPORTS.getName();
ServiceError serviceError = VPlexApiException.errors.storageViewRemoveStoragePortFailed(opName, ex);
failStep(completer, stepId, serviceError);
}
}
Aggregations