use of com.emc.storageos.blockorchestrationcontroller.VolumeDescriptor in project coprhd-controller by CoprHD.
the class BlockDeviceController method addStepsForUpdateConsistencyGroup.
/**
* Add Steps to add or remove volumes to the required consistency group
*
* @param workflow
* -- The Workflow being built
* @param waitFor
* -- Previous steps to waitFor
* @param volumesDescriptorsToAdd
* -- List<VolumeDescriptors> -- volumes of all types to be processed for adding to CG
* @param volumesDescriptorsToRemove
* -- List<VolumeDescriptors> -- volumes of all types to be processed for removing from CG
* @return last step added to waitFor
* @throws ControllerException
*/
public String addStepsForUpdateConsistencyGroup(Workflow workflow, String waitFor, List<VolumeDescriptor> volumesDescriptorsToAdd, List<VolumeDescriptor> volumesDescriptorsToRemove) throws ControllerException {
// Filter any BLOCK_DATAs that need to be added to CG.
List<VolumeDescriptor> addDescriptors = VolumeDescriptor.filterByType(volumesDescriptorsToAdd, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.BLOCK_DATA }, new VolumeDescriptor.Type[] {});
// Filter any BLOCK_DATAs that need to be removed from CG.
List<VolumeDescriptor> removeDescriptors = VolumeDescriptor.filterByType(volumesDescriptorsToRemove, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.BLOCK_DATA }, new VolumeDescriptor.Type[] {});
// We need at least one volume to check, so either get it from
// the add descriptors or the delete descriptors.
VolumeDescriptor firstVolume = null;
if (!addDescriptors.isEmpty()) {
firstVolume = addDescriptors.get(0);
} else if (!removeDescriptors.isEmpty()) {
firstVolume = removeDescriptors.get(0);
} else {
_log.warn("No volumes to add or remove from CG, skip step.");
// No volumes to be added or removed, just return.
return waitFor;
}
if (NullColumnValueGetter.isNullURI(firstVolume.getConsistencyGroupURI())) {
_log.warn(String.format("Volume (%s) has a null CG reference, skip step.", firstVolume.getVolumeURI()));
return waitFor;
}
// Check for SRDF
if (firstVolume.getType() != null) {
if (VolumeDescriptor.Type.SRDF_SOURCE.toString().equalsIgnoreCase(firstVolume.getType().toString()) || VolumeDescriptor.Type.SRDF_TARGET.toString().equalsIgnoreCase(firstVolume.getType().toString()) || VolumeDescriptor.Type.SRDF_EXISTING_SOURCE.toString().equalsIgnoreCase(firstVolume.getType().toString())) {
_log.warn(String.format("Volume (%s) is of type SRDF, skip step.", firstVolume.getVolumeURI()));
return waitFor;
}
}
// We want the map to contain both the volumes to ADD and REMOVE segregated by device and also by CG.
// The map will look like the below:
// Device URI
// --> CG URI
// ----> ADD -> List of Volumes to Add from this CG for this device
// ----> REMOVE -> List of Volumes to Remove from this CG for this device
Map<URI, Map<URI, Map<String, List<URI>>>> deviceToCGMap = createDeviceToCGMapFromDescriptors(addDescriptors, removeDescriptors);
// Distill the steps down to Device -> CG -> ADD and REMOVE volumes
for (Map.Entry<URI, Map<URI, Map<String, List<URI>>>> deviceEntry : deviceToCGMap.entrySet()) {
URI deviceURI = deviceEntry.getKey();
Map<URI, Map<String, List<URI>>> volumesToUpdateByCG = deviceEntry.getValue();
for (Map.Entry<URI, Map<String, List<URI>>> cgEntry : volumesToUpdateByCG.entrySet()) {
URI consistencyGroupURI = cgEntry.getKey();
List<URI> volumesToAdd = cgEntry.getValue().get(ADD_VOLUMES_TO_CG_KEY);
List<URI> volumesToRemove = cgEntry.getValue().get(REMOVE_VOLUMES_FROM_CG_KEY);
waitFor = workflow.createStep(UPDATE_CONSISTENCY_GROUP_STEP_GROUP, String.format("Updating consistency group %s", consistencyGroupURI), waitFor, deviceURI, getDeviceType(deviceURI), this.getClass(), new Workflow.Method("updateConsistencyGroup", deviceURI, consistencyGroupURI, volumesToAdd, volumesToRemove), rollbackMethodNullMethod(), null);
if (volumesToAdd != null) {
_log.info(String.format("Step created for adding volumes [%s] to CG [%s] on device [%s]", Joiner.on("\t").join(volumesToAdd), consistencyGroupURI, deviceURI));
}
if (volumesToRemove != null) {
_log.info(String.format("Step created for removing volumes [%s] from CG [%s] on device [%s]", Joiner.on("\t").join(volumesToRemove), consistencyGroupURI, deviceURI));
}
}
}
return waitFor;
}
use of com.emc.storageos.blockorchestrationcontroller.VolumeDescriptor in project coprhd-controller by CoprHD.
the class VolumeCreateWorkflowCompleter method handleBlockVolumeErrors.
private void handleBlockVolumeErrors(DbClient dbClient) {
for (VolumeDescriptor volumeDescriptor : VolumeDescriptor.getDescriptors(_volumeDescriptors, VolumeDescriptor.Type.BLOCK_DATA)) {
Volume volume = dbClient.queryObject(Volume.class, volumeDescriptor.getVolumeURI());
if (volume != null && (volume.getNativeId() == null || volume.getNativeId().equals(""))) {
_log.info("No native id was present on volume {}, marking inactive", volume.getLabel());
dbClient.markForDeletion(volume);
}
}
}
use of com.emc.storageos.blockorchestrationcontroller.VolumeDescriptor in project coprhd-controller by CoprHD.
the class ReplicaDeviceController method addStepsForCreateVolumes.
@Override
public String addStepsForCreateVolumes(Workflow workflow, String waitFor, List<VolumeDescriptor> volumes, String taskId) throws InternalException {
// Get the list of descriptors which represent source volumes that have
// just been created and added to CG possibly
List<VolumeDescriptor> volumeDescriptors = VolumeDescriptor.filterByType(volumes, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.BLOCK_DATA, VolumeDescriptor.Type.SRDF_SOURCE, VolumeDescriptor.Type.SRDF_EXISTING_SOURCE }, null);
// If no source volumes, just return
if (volumeDescriptors.isEmpty()) {
log.info("No replica steps required");
return waitFor;
}
// Get the consistency group. If no consistency group for
// any source volumes, just return. Get CG from any descriptor.
URI cgURI = null;
for (VolumeDescriptor descriptor : volumeDescriptors) {
Volume volume = _dbClient.queryObject(Volume.class, descriptor.getVolumeURI());
if (volume == null || !volume.isInCG() || !(ControllerUtils.isVmaxVolumeUsing803SMIS(volume, _dbClient) || ControllerUtils.isNotInRealVNXRG(volume, _dbClient))) {
log.info("No replica steps required for volume: " + descriptor.getVolumeURI());
continue;
}
log.info("CG URI:{}", volume.getConsistencyGroup());
cgURI = volume.getConsistencyGroup();
}
// Reason:Provisioning new volumes for VPLEX/RP CG in Application does not add backend volume to RG
if (!NullColumnValueGetter.isNullURI(cgURI)) {
BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, cgURI);
if (!cg.getArrayConsistency() && isBackendVolumeForVplexOrRp(volumes)) {
log.info("No replica steps required for CG {} as array consistency is disabled.", cg.getLabel());
return waitFor;
}
} else {
log.info("No replica steps required because no volumes had CG");
return waitFor;
}
List<VolumeDescriptor> nonSrdfVolumeDescriptors = VolumeDescriptor.filterByType(volumes, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.BLOCK_DATA }, null);
List<VolumeDescriptor> srdfSourceVolumeDescriptors = VolumeDescriptor.filterByType(volumes, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.SRDF_SOURCE, VolumeDescriptor.Type.SRDF_EXISTING_SOURCE }, null);
if (srdfSourceVolumeDescriptors.isEmpty()) {
waitFor = createReplicaIfCGHasReplica(workflow, waitFor, nonSrdfVolumeDescriptors, cgURI);
} else {
// Create Replica for SRDF R1 and R2 if any replica available already
log.debug("srdfSourceVolumeDescriptors :{}", srdfSourceVolumeDescriptors);
List<VolumeDescriptor> srdfTargetVolumeDescriptors = VolumeDescriptor.filterByType(volumes, new VolumeDescriptor.Type[] { VolumeDescriptor.Type.SRDF_TARGET }, null);
log.debug("srdfTargetVolumeDescriptors :{}", srdfTargetVolumeDescriptors);
// Create replica for R1
waitFor = createReplicaIfCGHasReplica(workflow, waitFor, srdfSourceVolumeDescriptors, cgURI);
// Create replica for R2
URI targetVolumeCG = SRDFUtils.getTargetVolumeCGFromSourceCG(_dbClient, cgURI);
if (targetVolumeCG != null) {
waitFor = createReplicaIfCGHasReplica(workflow, waitFor, srdfTargetVolumeDescriptors, targetVolumeCG);
}
}
return waitFor;
}
use of com.emc.storageos.blockorchestrationcontroller.VolumeDescriptor in project coprhd-controller by CoprHD.
the class VPlexDeviceController method buildArrayMap.
/**
* Build a map of URI to cached StorageSystem for the underlying arrays.
*
* @param descriptors
* @param VolmeDescriptor.Type
* used to filter descriptors
* @return Map<arrayURI, StorageSystem>
*/
private Map<URI, StorageSystem> buildArrayMap(List<VolumeDescriptor> descriptors, VolumeDescriptor.Type type) {
Map<URI, StorageSystem> arrayMap = new HashMap<URI, StorageSystem>();
// Get only the descriptors for the type if specified..
if (type != null) {
descriptors = VolumeDescriptor.filterByType(descriptors, new VolumeDescriptor.Type[] { type }, new VolumeDescriptor.Type[] {});
}
for (VolumeDescriptor desc : descriptors) {
if (arrayMap.containsKey(desc.getDeviceURI()) == false) {
StorageSystem array = getDataObject(StorageSystem.class, desc.getDeviceURI(), _dbClient);
arrayMap.put(desc.getDeviceURI(), array);
}
}
return arrayMap;
}
use of com.emc.storageos.blockorchestrationcontroller.VolumeDescriptor 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