use of com.emc.storageos.vplex.api.VPlexApiClient 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.vplex.api.VPlexApiClient in project coprhd-controller by CoprHD.
the class VPlexDeviceController method addStepsForDeleteVolumes.
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.impl.vplex.VplexController#deleteVolumes(java.net.URI, java.util.List,
* java.lang.String)
* <p>
* NOTE: The VolumeDescriptor list will not include the underlying Volumes. These have to be
* added to the VolumeDescriptor list before returning.
*/
@Override
public String addStepsForDeleteVolumes(Workflow workflow, String waitFor, List<VolumeDescriptor> volumes, String taskId) throws ControllerException {
try {
// Filter to get only the VPlex volumes.
List<VolumeDescriptor> vplexVolumes = VolumeDescriptor.filterByType(volumes, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.VPLEX_VIRT_VOLUME }, new VolumeDescriptor.Type[] {});
// If there are no VPlex volumes, just return
if (vplexVolumes.isEmpty()) {
return waitFor;
}
// Check to see if there are any volumes flagged to not be fully deleted.
// This will still remove the volume from its VPLEX CG and also clean up
// any Mirrors but will leave the Virtual Volume intact on the VPLEX.
List<VolumeDescriptor> doNotDeleteDescriptors = VolumeDescriptor.getDoNotDeleteDescriptors(vplexVolumes);
List<URI> doNotFullyDeleteVolumeList = VolumeDescriptor.getVolumeURIs(doNotDeleteDescriptors);
List<URI> allVplexVolumeURIs = VolumeDescriptor.getVolumeURIs(vplexVolumes);
// Segregate by device.
Map<URI, List<VolumeDescriptor>> vplexMap = VolumeDescriptor.getDeviceMap(vplexVolumes);
// but subsequent steps will wait on all the delete virtual volumes operations to complete.
for (URI vplexURI : vplexMap.keySet()) {
String nextStepWaitFor = waitFor;
StorageSystem vplexSystem = getDataObject(StorageSystem.class, vplexURI, _dbClient);
// First validate that the backend volumes for these VPLEX volumes are
// the actual volumes used by the VPLEX volume on the VPLEX system. We
// add this verification in case changes were made outside ViPR, such
// as a migration, that caused the backend volumes to change. We don't
// want to delete a backend volume that may in fact be used some other
// VPLEX volume.
List<URI> vplexVolumeURIs = VolumeDescriptor.getVolumeURIs(vplexMap.get(vplexURI));
for (URI vplexVolumeURI : vplexVolumeURIs) {
Volume vplexVolume = _dbClient.queryObject(Volume.class, vplexVolumeURI);
if (vplexVolume == null || vplexVolume.getInactive() == true) {
continue;
}
// Skip validation if the volume was never successfully created.
if (vplexVolume.getDeviceLabel() == null) {
_log.info("Volume {} with Id {} was never created on the VPLEX as device label is null " + "hence skip validation on delete", vplexVolume.getLabel(), vplexVolume.getId());
continue;
}
// backend volume deletion failed.
try {
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplexSystem, _dbClient);
client.findVirtualVolume(vplexVolume.getDeviceLabel(), vplexVolume.getNativeId());
} catch (VPlexApiException ex) {
if (ex.getServiceCode() == ServiceCode.VPLEX_CANT_FIND_REQUESTED_VOLUME) {
_log.info("VPlex virtual volume: " + vplexVolume.getNativeId() + " has already been deleted; will skip validation");
continue;
} else {
_log.error("Exception finding Virtual Volume", ex);
throw ex;
}
}
createWorkflowStepToValidateVPlexVolume(workflow, vplexSystem, vplexVolumeURI, waitFor);
nextStepWaitFor = VALIDATE_VPLEX_VOLUME_STEP;
}
// If there are VPlex Volumes fronting SRDF targets, handle them.
// They will need to be removed from the CG that represents the SRDF targets.
List<URI> volsForTargetCG = VPlexSrdfUtil.returnVplexSrdfTargets(_dbClient, vplexVolumeURIs);
if (!volsForTargetCG.isEmpty()) {
URI volURI = volsForTargetCG.get(0);
Volume vol = VPlexControllerUtils.getDataObject(Volume.class, volURI, _dbClient);
ConsistencyGroupManager consistencyGroupManager = getConsistencyGroupManager(vol);
nextStepWaitFor = consistencyGroupManager.addStepsForRemovingVolumesFromSRDFTargetCG(workflow, vplexSystem, volsForTargetCG, nextStepWaitFor);
}
workflow.createStep(VPLEX_STEP, String.format("Delete VPlex Virtual Volumes:%n%s", BlockDeviceController.getVolumesMsg(_dbClient, vplexVolumeURIs)), nextStepWaitFor, vplexURI, DiscoveredDataObject.Type.vplex.name(), this.getClass(), deleteVirtualVolumesMethod(vplexURI, vplexVolumeURIs, doNotFullyDeleteVolumeList), rollbackMethodNullMethod(), null);
}
// Make a Map of array URI to StorageSystem
Map<URI, StorageSystem> arrayMap = new HashMap<URI, StorageSystem>();
// Make a Map of StorageSystem to a list of Volume URIs to be deleted for the next Step.
Map<URI, List<URI>> arrayVolumesMap = new HashMap<URI, List<URI>>();
// Make a list of ExportGroups that is used.
List<URI> exportGroupList = new ArrayList<URI>();
// Create a series of steps to remove the volume from the Export Groups.
// We leave the Export Groups, anticipating they will be used for other
// volumes or used later.
List<URI> backendVolURIs = new ArrayList<URI>();
for (URI vplexVolumeURI : allVplexVolumeURIs) {
Volume vplexVolume = _dbClient.queryObject(Volume.class, vplexVolumeURI);
if ((vplexVolume == null) || (vplexVolume.getInactive()) || (vplexVolume.isIngestedVolumeWithoutBackend(_dbClient)) || doNotFullyDeleteVolumeList.contains(vplexVolumeURI)) {
continue;
}
if (null == vplexVolume.getAssociatedVolumes()) {
_log.warn("VPLEX volume {} has no backend volumes. It was possibly ingested 'Virtual Volume Only'.", vplexVolume.forDisplay());
} else {
for (String assocVolumeId : vplexVolume.getAssociatedVolumes()) {
URI assocVolumeURI = new URI(assocVolumeId);
Volume volume = _dbClient.queryObject(Volume.class, assocVolumeURI);
if (volume == null || volume.getInactive() == true) {
continue;
}
StorageSystem array = arrayMap.get(volume.getStorageController());
if (array == null) {
array = _dbClient.queryObject(StorageSystem.class, volume.getStorageController());
arrayMap.put(array.getId(), array);
}
if (arrayVolumesMap.get(array.getId()) == null) {
arrayVolumesMap.put(array.getId(), new ArrayList<URI>());
}
arrayVolumesMap.get(array.getId()).add(volume.getId());
backendVolURIs.add(volume.getId());
}
}
// hence the backend volume for that mirror needs to be deleted as well.
if (vplexVolume.getMirrors() != null && !(vplexVolume.getMirrors().isEmpty())) {
for (String mirrorId : vplexVolume.getMirrors()) {
VplexMirror vplexMirror = _dbClient.queryObject(VplexMirror.class, URI.create(mirrorId));
if (vplexMirror == null || vplexMirror.getInactive() == true || vplexMirror.getAssociatedVolumes() == null) {
continue;
}
for (String assocVolumeId : vplexMirror.getAssociatedVolumes()) {
URI assocVolumeURI = new URI(assocVolumeId);
Volume volume = _dbClient.queryObject(Volume.class, assocVolumeURI);
if (volume == null || volume.getInactive() == true) {
continue;
}
StorageSystem array = arrayMap.get(volume.getStorageController());
if (array == null) {
array = _dbClient.queryObject(StorageSystem.class, volume.getStorageController());
arrayMap.put(array.getId(), array);
}
if (arrayVolumesMap.get(array.getId()) == null) {
arrayVolumesMap.put(array.getId(), new ArrayList<URI>());
}
arrayVolumesMap.get(array.getId()).add(volume.getId());
backendVolURIs.add(volume.getId());
}
}
}
}
waitFor = VPLEX_STEP;
if (vplexAddUnexportVolumeWfSteps(workflow, VPLEX_STEP, backendVolURIs, exportGroupList)) {
waitFor = UNEXPORT_STEP;
}
return waitFor;
} catch (Exception ex) {
throw VPlexApiException.exceptions.addStepsForDeleteVolumesFailed(ex);
}
}
use of com.emc.storageos.vplex.api.VPlexApiClient 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.vplex.api.VPlexApiClient 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);
}
}
use of com.emc.storageos.vplex.api.VPlexApiClient in project coprhd-controller by CoprHD.
the class VPlexDeviceController method detachMirror.
/**
* Called to detach the remote mirror for a distributed VPLEX volume prior
* to restoring a native snapshot of the backend source volume.
*
* @param vplexURI
* The URI of the VPLEX system.
* @param vplexVolumeURI
* The URI of the distributed VPLEX volume.
* @param cgURI
* The URI of the volume's CG or null.
* @param stepId
* The workflow step identifier.
*/
public void detachMirror(URI vplexURI, URI vplexVolumeURI, URI cgURI, String stepId) {
_log.info("Executing detach mirror of VPLEX volume {} on VPLEX {}", new Object[] { vplexVolumeURI, vplexURI });
String detachedDeviceName = "";
try {
// Initialize the data that will tell RB what to do in
// case of failure.
Map<String, String> stepData = new HashMap<String, String>();
stepData.put(REATTACH_MIRROR, Boolean.FALSE.toString());
stepData.put(ADD_BACK_TO_CG, Boolean.FALSE.toString());
_workflowService.storeStepData(stepId, stepData);
// Update workflow step.
WorkflowStepCompleter.stepExecuting(stepId);
// Get the API client.
StorageSystem vplexSystem = getDataObject(StorageSystem.class, vplexURI, _dbClient);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplexSystem, _dbClient);
_log.info("Got VPLEX API client");
// Get the VPLEX volume.
Volume vplexVolume = getDataObject(Volume.class, vplexVolumeURI, _dbClient);
String vplexVolumeName = vplexVolume.getDeviceLabel();
_log.info("Got VPLEX volume");
// COP-17337 : If device length is greater than 47 character then VPLEX does
// not allow reattaching mirror. This is mostly going to be the case for the
// distributed volume with XIO back-end on both legs. Temporarily rename the
// device to 47 characters and then detach the mirror. Now when we reattach
// the device name will not exceed the limit. We must do this before we detach
// so that the device name is the same for the detach/attach operations. If
// we try to rename prior to reattaching the mirror, COP-25581 will result.
VPlexVirtualVolumeInfo vvInfo = client.findVirtualVolume(vplexVolumeName, vplexVolume.getNativeId());
if (vvInfo != null) {
String distDeviceName = vvInfo.getSupportingDevice();
if (distDeviceName.length() > VPlexApiConstants.MAX_DEVICE_NAME_LENGTH_FOR_ATTACH_MIRROR) {
String modifiedName = distDeviceName.substring(0, VPlexApiConstants.MAX_DEVICE_NAME_LENGTH_FOR_ATTACH_MIRROR);
_log.info("Temporarily renaming the distributed device from {} to {} as VPLEX expects the name " + " to be 47 characters or less when we reattach the mirror.", distDeviceName, modifiedName);
client.renameDistributedDevice(distDeviceName, modifiedName);
stepData.put(RESTORE_DEVICE_NAME, distDeviceName);
}
} else {
_log.error("Can't find volume {}:{} to detach mirror.", vplexVolumeName, vplexVolumeURI);
throw VPlexApiException.exceptions.cantFindVolumeForDeatchMirror(vplexVolume.forDisplay());
}
// detaching the remote mirror.
if (cgURI != null) {
ConsistencyGroupManager consistencyGroupManager = getConsistencyGroupManager(vplexVolume);
consistencyGroupManager.removeVolumeFromCg(cgURI, vplexVolume, client, false);
stepData.put(ADD_BACK_TO_CG, Boolean.TRUE.toString());
_workflowService.storeStepData(stepId, stepData);
_log.info("Removed volumes from consistency group.");
}
// Detach the mirror.
detachedDeviceName = client.detachMirrorFromDistributedVolume(vplexVolumeName, null);
stepData.put(DETACHED_DEVICE, detachedDeviceName);
stepData.put(REATTACH_MIRROR, Boolean.TRUE.toString());
_workflowService.storeStepData(stepId, stepData);
_log.info("Detached the mirror");
updateThinProperty(client, vplexSystem, vplexVolume);
// Update workflow step state to success.
WorkflowStepCompleter.stepSucceded(stepId);
_log.info("Updated workflow step state to success");
} catch (VPlexApiException vae) {
_log.error("Exception detaching mirror for VPLEX distributed volume: " + vae.getMessage(), vae);
WorkflowStepCompleter.stepFailed(stepId, vae);
} catch (Exception e) {
_log.error("Exception detaching mirror for VPLEX distributed volume: " + e.getMessage(), e);
WorkflowStepCompleter.stepFailed(stepId, VPlexApiException.exceptions.failedDetachingVPlexVolumeMirror(detachedDeviceName, vplexVolumeURI.toString(), e));
}
}
Aggregations