use of com.emc.storageos.db.client.model.VplexMirror in project coprhd-controller by CoprHD.
the class VPlexDeviceController method addStepsForPostDeleteVolumes.
/**
* Add step to Workflow for post - delete clean of Virtual Volumes (i.e. marking them inactive).
*
* @param workflow
* -- Workflow
* @param waitFor
* -- String waitFor of previous step, we wait on this to complete
* @param volumes
* -- List of VolumeDescriptors
* @param taskId
* -- String overall task id.
* @param completer
* -- VolumeWorkflowCompleter
* @return -- Returns waitFor of next step
*/
@Override
public String addStepsForPostDeleteVolumes(Workflow workflow, String waitFor, List<VolumeDescriptor> volumes, String taskId, VolumeWorkflowCompleter completer) {
// Filter to get only the VPlex volumes.
List<VolumeDescriptor> vplexVolumes = VolumeDescriptor.filterByType(volumes, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.VPLEX_VIRT_VOLUME }, new VolumeDescriptor.Type[] {});
// Check to see if there are any volumes flagged to not be fully deleted.
// Any flagged volumes will be removed from the list of volumes to delete.
List<VolumeDescriptor> descriptorsToRemove = VolumeDescriptor.getDoNotDeleteDescriptors(vplexVolumes);
vplexVolumes.removeAll(descriptorsToRemove);
String returnWaitFor = waitFor;
// If there are no VPlex volumes, just return
if (vplexVolumes.isEmpty()) {
return returnWaitFor;
}
// Segregate by device and loop over each VPLEX system.
// Sort the volumes by its system, and consistency group
Map<URI, Set<URI>> cgVolsMap = new HashMap<URI, Set<URI>>();
// Keep a separate map for determining if we should delete the VPLEX CG as part of the delete operation.
Map<URI, Set<URI>> cgVolsWithBackingVolsMap = new HashMap<URI, Set<URI>>();
Map<URI, List<VolumeDescriptor>> vplexMap = VolumeDescriptor.getDeviceMap(vplexVolumes);
for (URI vplexURI : vplexMap.keySet()) {
List<URI> vplexVolumeURIs = VolumeDescriptor.getVolumeURIs(vplexMap.get(vplexURI));
List<URI> forgetVolumeURIs = new ArrayList<URI>();
for (URI vplexVolumeURI : vplexVolumeURIs) {
Volume vplexVolume = getDataObject(Volume.class, vplexVolumeURI, _dbClient);
boolean inCG = false;
if (!NullColumnValueGetter.isNullURI(vplexVolume.getConsistencyGroup())) {
inCG = true;
}
if (null == vplexVolume.getAssociatedVolumes()) {
_log.warn("VPLEX volume {} has no backend volumes. It was possibly ingested 'Virtual Volume Only'.", vplexVolume.forDisplay());
} else {
for (String forgetVolumeId : vplexVolume.getAssociatedVolumes()) {
forgetVolumeURIs.add(URI.create(forgetVolumeId));
}
}
if (inCG) {
Set<URI> cgVols = cgVolsMap.get(vplexVolume.getConsistencyGroup());
if (cgVols == null) {
cgVolsMap.put(vplexVolume.getConsistencyGroup(), new HashSet<>());
cgVolsWithBackingVolsMap.put(vplexVolume.getConsistencyGroup(), new HashSet<>());
}
cgVolsMap.get(vplexVolume.getConsistencyGroup()).add(vplexVolumeURI);
cgVolsWithBackingVolsMap.get(vplexVolume.getConsistencyGroup()).add(vplexVolumeURI);
cgVolsWithBackingVolsMap.get(vplexVolume.getConsistencyGroup()).addAll(forgetVolumeURIs);
}
// Adding the VPLEX mirror backend volume to forgetVolumeURIs
if (vplexVolume.getMirrors() != null && !(vplexVolume.getMirrors().isEmpty())) {
for (String mirrorId : vplexVolume.getMirrors()) {
VplexMirror vplexMirror = _dbClient.queryObject(VplexMirror.class, URI.create(mirrorId));
if (null != vplexMirror && !vplexMirror.getInactive() && null != vplexMirror.getAssociatedVolumes()) {
for (String forgetVolumeId : vplexMirror.getAssociatedVolumes()) {
forgetVolumeURIs.add(URI.create(forgetVolumeId));
}
}
}
}
}
// Add a step to forget the backend volumes for the deleted
// VPLEX volumes on this VPLEX system.
addStepToForgetVolumes(workflow, vplexURI, forgetVolumeURIs, returnWaitFor);
}
// Get the VPlex Volume URIs and any VPLEX system URI. It does not matter which
// system as it the step simply marks ViPR volumes in the database inactive.
List<URI> allVplexVolumeURIs = VolumeDescriptor.getVolumeURIs(vplexVolumes);
URI vplexURI = vplexVolumes.get(0).getDeviceURI();
// Add a step to the Workflow to mark the Virtual Volumes inactive.
// Rollback does the same thing.
returnWaitFor = workflow.createStep(null, "Mark virtual volumes inactive", VOLUME_FORGET_STEP, vplexURI, DiscoveredDataObject.Type.vplex.name(), this.getClass(), markVolumesInactiveMethod(allVplexVolumeURIs), markVolumesInactiveMethod(allVplexVolumeURIs), null);
if (cgVolsMap.isEmpty()) {
return returnWaitFor;
}
Volume vol = getDataObject(Volume.class, allVplexVolumeURIs.get(0), _dbClient);
ConsistencyGroupManager consistencyGroupManager = getConsistencyGroupManager(vol);
// Generate step(s) to delete the VPLEX consistency groups
for (URI cgURI : cgVolsMap.keySet()) {
// Skip volumes that are part of a VPlex SRDF target CG, as it will
// be deleted earlier
List<URI> volURIs = new ArrayList<URI>(cgVolsMap.get(cgURI));
volURIs = VPlexSrdfUtil.filterOutVplexSrdfTargets(_dbClient, volURIs);
if (volURIs.isEmpty()) {
_log.info(String.format("CG %s has all VPLEX SRDF targets, skipping as CG should already be deleted", cgURI));
continue;
}
// find member volumes in the group
volURIs = new ArrayList<URI>(cgVolsWithBackingVolsMap.get(cgURI));
Volume firstVol = _dbClient.queryObject(Volume.class, volURIs.get(0));
URI storage = firstVol.getStorageController();
// delete CG from array
if (VPlexUtil.cgHasNoOtherVPlexVolumes(_dbClient, cgURI, volURIs)) {
_log.info(String.format("Adding step to delete the consistency group %s", cgURI));
returnWaitFor = consistencyGroupManager.addStepsForDeleteConsistencyGroup(workflow, returnWaitFor, storage, cgURI, false);
} else {
_log.info(String.format("Skipping add step to delete the consistency group %s. Consistency group " + "contains other VPLEX volumes that have not been accounted for.", cgURI));
}
}
return returnWaitFor;
}
use of com.emc.storageos.db.client.model.VplexMirror in project coprhd-controller by CoprHD.
the class VPlexDeviceController method findPromotedVolumeForMirror.
private Volume findPromotedVolumeForMirror(URI vplexMirrorURI, List<URI> promotees) {
VplexMirror mirror = _dbClient.queryObject(VplexMirror.class, vplexMirrorURI);
List<Volume> promotedVolumes = _dbClient.queryObject(Volume.class, promotees);
for (Volume promotee : promotedVolumes) {
OpStatusMap statusMap = promotee.getOpStatus();
for (Map.Entry<String, Operation> entry : statusMap.entrySet()) {
Operation operation = entry.getValue();
if (operation.getAssociatedResourcesField().contains(mirror.getId().toString())) {
return promotee;
}
}
}
throw new IllegalStateException("No volume available for the promotion of mirror " + mirror.getId());
}
use of com.emc.storageos.db.client.model.VplexMirror in project coprhd-controller by CoprHD.
the class VPlexDeviceController method rollbackPromoteMirror.
/**
* Here we try to delete the virtual volume thats created from the mirror and then try to
* reattach the mirror so as to leave the original state. This is only going to be best effort.
* Delete the volume objects that were created as promotees.
*
* @param promotees
* The URIs of the volumes that were supposed to be promoted from mirror.
* @param executeStepId
* step Id of the execute step
* @param stepId
* The stepId used for completion.
*
* @throws WorkflowException
* When an error occurs updating the workflow step
* state.
*/
public void rollbackPromoteMirror(URI vplexURI, URI mirrorURI, URI promoteeURI, String executeStepId, String stepId) throws WorkflowException {
try {
// Update step state to executing.
WorkflowStepCompleter.stepExecuting(stepId);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplexURI, _dbClient);
VplexMirror vplexMirror = _dbClient.queryObject(VplexMirror.class, mirrorURI);
// Get source volume for the mirror
Volume sourceVplexVolume = getDataObject(Volume.class, vplexMirror.getSource().getURI(), _dbClient);
String locality = null;
if (sourceVplexVolume.getAssociatedVolumes() != null && sourceVplexVolume.getAssociatedVolumes().size() > 1) {
locality = VPlexApiConstants.DISTRIBUTED_VIRTUAL_VOLUME;
} else {
locality = VPlexApiConstants.LOCAL_VIRTUAL_VOLUME;
}
// Delete the virtual volume that should have been created when we did detach mirror.
// Virtual volume is created with the same name as the device name.
// Delete the virtual volume only, donot tear down
client.destroyVirtualVolume(vplexMirror.getDeviceLabel());
// Attach the mirror device back to the source device
client.attachMirror(locality, sourceVplexVolume.getDeviceLabel(), vplexMirror.getDeviceLabel());
_log.info("Successfully re-attached mirror %s to the source volume %s during rollback. ", vplexMirror.getDeviceLabel(), sourceVplexVolume.getDeviceLabel());
} catch (Exception e) {
// If exception occurs that means mirror is already detached and we couldn't reattach
// So cleanup database objects related to a mirror.
VplexMirror vplexMirror = _dbClient.queryObject(VplexMirror.class, mirrorURI);
Volume sourceVplexVolume = getDataObject(Volume.class, vplexMirror.getSource().getURI(), _dbClient);
// Remove mirror from the source VPLEX volume
sourceVplexVolume.getMirrors().remove(vplexMirror.getId().toString());
_dbClient.updateObject(sourceVplexVolume);
_log.info("Removed mirror %s from source volume %s", mirrorURI, sourceVplexVolume.getId());
// Delete mirror and associated volume from database
if (null != vplexMirror.getAssociatedVolumes()) {
for (String assocVolumeId : vplexMirror.getAssociatedVolumes()) {
Volume volume = _dbClient.queryObject(Volume.class, URI.create(assocVolumeId));
_dbClient.removeObject(volume);
}
}
// Delete the mirror object
_dbClient.removeObject(vplexMirror);
_log.error("Error during rollback of promote mirror: {}", e.getMessage(), e);
} finally {
// Delete the volume that was supposed to be promoted volume
Volume volume = _dbClient.queryObject(Volume.class, promoteeURI);
_dbClient.removeObject(volume);
// Return success so rollback continues
WorkflowStepCompleter.stepSucceded(stepId);
}
}
use of com.emc.storageos.db.client.model.VplexMirror in project coprhd-controller by CoprHD.
the class VPlexDeviceController method rollbackDeleteMirrorDevice.
/**
* Here we will try to reattach mirror device. If we cannot reattach then the mirror
* is already detached and mirror related objects will be removed from the database.
*
* @param vplexURI
* URI of the VPlex StorageSystem
* @param mirrorURI
* 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 rollbackDeleteMirrorDevice(URI vplexURI, URI mirrorURI, String stepId) throws WorkflowException {
try {
// Update step state to executing.
WorkflowStepCompleter.stepExecuting(stepId);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplexURI, _dbClient);
VplexMirror vplexMirror = _dbClient.queryObject(VplexMirror.class, mirrorURI);
// Get source volume for the mirror
Volume sourceVplexVolume = getDataObject(Volume.class, vplexMirror.getSource().getURI(), _dbClient);
String locality = null;
if (sourceVplexVolume.getAssociatedVolumes() != null && sourceVplexVolume.getAssociatedVolumes().size() > 1) {
locality = VPlexApiConstants.DISTRIBUTED_VIRTUAL_VOLUME;
} else {
locality = VPlexApiConstants.LOCAL_VIRTUAL_VOLUME;
}
// Try to re-attach the mirror device back to the source device
client.attachMirror(locality, sourceVplexVolume.getDeviceLabel(), vplexMirror.getDeviceLabel());
_log.info("Successfully re-attached mirror %s to the source volume %s during delete mirror rollback. ", vplexMirror.getDeviceLabel(), sourceVplexVolume.getDeviceLabel());
} catch (Exception e) {
// If exception occurs that means mirror is already detached and we couldn't reattach
// So cleanup database objects related to a mirror.
VplexMirror vplexMirror = _dbClient.queryObject(VplexMirror.class, mirrorURI);
Volume sourceVplexVolume = getDataObject(Volume.class, vplexMirror.getSource().getURI(), _dbClient);
// Remove mirror from the source VPLEX volume
sourceVplexVolume.getMirrors().remove(vplexMirror.getId().toString());
_dbClient.updateObject(sourceVplexVolume);
_log.info("Removed mirror %s from source volume %s", mirrorURI, sourceVplexVolume.getId());
// Delete mirror and associated volume from database
if (null != vplexMirror.getAssociatedVolumes()) {
for (String assocVolumeId : vplexMirror.getAssociatedVolumes()) {
Volume volume = _dbClient.queryObject(Volume.class, URI.create(assocVolumeId));
if (null != volume) {
_dbClient.removeObject(volume);
}
}
}
// Delete the mirror object
_dbClient.removeObject(vplexMirror);
_log.error("Error during rollback of promote mirror: {}", e.getMessage(), e);
} finally {
// Return success so rollback continues
WorkflowStepCompleter.stepSucceded(stepId);
}
}
use of com.emc.storageos.db.client.model.VplexMirror in project coprhd-controller by CoprHD.
the class VPlexDeviceController method deleteVirtualVolumes.
/**
* A workflow Step to delete VPLex Virtual volumes.
* This Step is also used to rollback create virtual volumes.
* NOTE NOTE: The parameters here must match deleteVirtualVolumesMethod above (except stepId).
*
* @param vplexURI
* @param volumeURIs
* @param doNotFullyDeleteVolumeList
* @param stepId
* @throws WorkflowException
*/
public void deleteVirtualVolumes(URI vplexURI, List<URI> volumeURIs, List<URI> doNotFullyDeleteVolumeList, String stepId) throws WorkflowException {
try {
WorkflowStepCompleter.stepExecuting(stepId);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplexURI, _dbClient);
StringBuilder errMsgBuilder = new StringBuilder();
boolean failed = false;
// Loop deleting each volume by name (the deviceLabel in the Volume).
for (URI volumeURI : volumeURIs) {
Volume volume = _dbClient.queryObject(Volume.class, volumeURI);
if (volume == null || volume.getInactive() == true) {
continue;
}
// Skip this operation if the volume has already been deleted
if (volume.getDeviceLabel() == null) {
_log.info("Volume {} with Id {} was never created on the Vplex as device label is null " + "hence skip volume delete on VPLEX", volume.getLabel(), volume.getId());
continue;
}
try {
client.findVirtualVolume(volume.getDeviceLabel(), volume.getNativeId());
} catch (VPlexApiException ex) {
if (ex.getServiceCode() == ServiceCode.VPLEX_CANT_FIND_REQUESTED_VOLUME) {
_log.info("VPlex virtual volume: " + volume.getNativeId() + " has already been deleted; will skip deletion of virtual volume");
continue;
} else {
_log.error("Exception finding Virtual Volume", ex);
throw ex;
}
}
try {
if (volume.getNativeId() != null) {
// Volumes in consistency groups must be removed.
BlockConsistencyGroup cg = null;
if (!NullColumnValueGetter.isNullURI(volume.getConsistencyGroup())) {
cg = getDataObject(BlockConsistencyGroup.class, volume.getConsistencyGroup(), _dbClient);
}
if (cg != null) {
// Call the appropriate ConsistencyGroupManager to delete the CG volume
ConsistencyGroupManager consistencyGroupManager = getConsistencyGroupManager(volume);
consistencyGroupManager.deleteConsistencyGroupVolume(vplexURI, volume, cg);
}
// to delete volume.
if (doNotFullyDeleteVolumeList == null || doNotFullyDeleteVolumeList.isEmpty() || !doNotFullyDeleteVolumeList.contains(volume.getId())) {
// We only retry a dismantle failure on volumes created
// in ViPR as the retry code relies on the well-known ViPR
// naming conventions and virtual volume structure to find
// VPLEX artifacts related to the volume being deleted.
_log.info(String.format("Deleting VPlex virtual volume %s (%s)", volume.getDeviceLabel(), volume.getNativeId()));
boolean isIngestedWithoutBackend = volume.isIngestedVolumeWithoutBackend(_dbClient);
client.deleteVirtualVolume(volume.getDeviceLabel(), !isIngestedWithoutBackend, !isIngestedWithoutBackend);
}
// Record VPLEX volume deleted event.
recordBourneVolumeEvent(volume.getId(), OperationTypeEnum.DELETE_BLOCK_VOLUME.getEvType(true), Operation.Status.ready, OperationTypeEnum.DELETE_BLOCK_VOLUME.getDescription());
if (volume.getMirrors() != null && !(volume.getMirrors().isEmpty())) {
for (String mirrorId : volume.getMirrors()) {
VplexMirror mirror = _dbClient.queryObject(VplexMirror.class, URI.create(mirrorId));
if (null != mirror) {
_log.info("Marking mirror {} {} for deletion.", mirror.getId(), mirror.getDeviceLabel());
_dbClient.removeObject(mirror);
}
}
}
}
} catch (Exception ex) {
_log.error("Exception deleting Virtual Volume: " + volumeURI, ex);
// Record VPLEX volume deletion failed event.
recordBourneVolumeEvent(volume.getId(), OperationTypeEnum.DELETE_BLOCK_VOLUME.getEvType(false), Operation.Status.error, OperationTypeEnum.DELETE_BLOCK_VOLUME.getDescription());
// Update error message
if (errMsgBuilder.length() != 0) {
errMsgBuilder.append("\n");
} else {
errMsgBuilder.append("Exception deleting vplex virtual volume(s):\n");
}
errMsgBuilder.append(volume.getLabel());
errMsgBuilder.append(":");
errMsgBuilder.append(ex.getMessage());
failed = true;
}
}
if (failed) {
String opName = ResourceOperationTypeEnum.DELETE_VIRTUAL_VOLUME.getName();
ServiceError serviceError = VPlexApiException.errors.jobFailedOp(opName);
serviceError.setMessage(errMsgBuilder.toString());
WorkflowStepCompleter.stepFailed(stepId, serviceError);
} else {
WorkflowStepCompleter.stepSucceded(stepId);
}
} catch (VPlexApiException vae) {
_log.error("Exception deleting VPlex Virtual Volume: " + vae.getMessage(), vae);
WorkflowStepCompleter.stepFailed(stepId, vae);
} catch (Exception ex) {
_log.error("Exception deleting VPlex Virtual Volume: " + ex.getMessage(), ex);
String opName = ResourceOperationTypeEnum.DELETE_VIRTUAL_VOLUME.getName();
ServiceError serviceError = VPlexApiException.errors.deleteVirtualVolumesFailed(opName, ex);
WorkflowStepCompleter.stepFailed(stepId, serviceError);
}
}
Aggregations