use of com.emc.storageos.api.service.impl.resource.utils.RPVPlexMigration in project coprhd-controller by CoprHD.
the class RPBlockServiceApiImpl method changeVolumeVirtualPool.
@Override
public TaskList changeVolumeVirtualPool(List<Volume> volumes, VirtualPool vpool, VirtualPoolChangeParam vpoolChangeParam, String taskId) throws InternalException {
TaskList taskList = new TaskList();
StringBuffer notSuppReasonBuff = new StringBuffer();
notSuppReasonBuff.setLength(0);
// Get the first volume for the change vpool operation
Volume firstVolume = volumes.get(0);
// Get the current vpool from the first volume
VirtualPool currentVpool = _dbClient.queryObject(VirtualPool.class, firstVolume.getVirtualPool());
// Container for RP+VPLEX migrations (if there are any)
List<RPVPlexMigration> validMigrations = new ArrayList<RPVPlexMigration>();
if (firstVolume.checkForRp() && firstVolume.checkPersonality(Volume.PersonalityTypes.METADATA)) {
boolean vplex = RPHelper.isVPlexVolume(firstVolume, _dbClient);
if (vplex) {
if (VirtualPoolChangeAnalyzer.vpoolChangeRequiresMigration(currentVpool, vpool)) {
// Allow the VPLEX Data Migration operation for the RP+VPLEX Journal
// to proceed via the VPLEX Block Service.
taskList.getTaskList().addAll(vplexBlockServiceApiImpl.changeVolumeVirtualPool(volumes, vpool, vpoolChangeParam, taskId).getTaskList());
}
}
} else if (firstVolume.checkForRp() && !VirtualPool.vPoolSpecifiesProtection(vpool)) {
taskList = createTasksForVolumes(vpool, volumes, taskId);
removeProtection(volumes, vpool, taskId);
} else if (VirtualPoolChangeAnalyzer.isSupportedRPVPlexMigrationVirtualPoolChange(firstVolume, currentVpool, vpool, _dbClient, notSuppReasonBuff, validMigrations)) {
taskList.getTaskList().addAll(rpVPlexDataMigration(volumes, vpool, taskId, validMigrations, vpoolChangeParam).getTaskList());
} else {
// until CTRL-1347 and CTRL-5609 are fixed.
if (volumes.size() == 1) {
taskList = createTasksForVolumes(vpool, volumes, taskId);
changeVolumeVirtualPool(firstVolume.getStorageController(), firstVolume, vpool, vpoolChangeParam, taskId);
} else {
throw APIException.methodNotAllowed.notSupportedWithReason("Multiple volume change virtual pool is currently not supported for RecoverPoint. " + "Please select one volume at a time.");
}
}
return taskList;
}
use of com.emc.storageos.api.service.impl.resource.utils.RPVPlexMigration in project coprhd-controller by CoprHD.
the class RPBlockServiceApiImpl method rpVPlexJournalMigrations.
/**
* Special Journal migration step needed as Journals belong to a CG and need to be
* gathered from the CG. These migrations are always single migrations.
*
* @param journalMigrationsExist Boolean to determine if journal migrations exist
* @param journalVpoolMigrations List of RPVPlexMigrations for Journals
* @param singleMigrations Container to store all single migrations
* @param cgURIs Set of URIs of all the CGs from the request
* @param logMigrations String buffer for logging
*/
private void rpVPlexJournalMigrations(boolean journalMigrationsExist, List<RPVPlexMigration> journalVpoolMigrations, Map<Volume, VirtualPool> singleMigrations, Set<URI> cgURIs, StringBuffer logMigrations) {
if (journalMigrationsExist) {
for (URI cgURI : cgURIs) {
BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, cgURI);
// Get all Journal volumes from the CG.
List<Volume> journalVolumes = RPHelper.getCgVolumes(_dbClient, cg.getId(), Volume.PersonalityTypes.METADATA.name());
for (Volume journalVolume : journalVolumes) {
// Check to see if this Journal volume qualifies for migration
RPVPlexMigration journalMigration = null;
for (RPVPlexMigration migration : journalVpoolMigrations) {
if (journalVolume.getVirtualArray().equals(migration.getVarray())) {
// Need to make sure we're migrating the right Journal, so check to make sure the copy names match
boolean isSourceJournal = migration.getSubType().equals(Volume.PersonalityTypes.SOURCE) ? true : false;
String copyName = RPHelper.getCgCopyName(_dbClient, cg, migration.getVarray(), isSourceJournal);
if (journalVolume.getRpCopyName().equals(copyName)) {
journalMigration = migration;
break;
}
}
}
// a new task for the operation.
if (journalMigration != null) {
// Make sure the journal volume is not involved in another task. If it is, an exception will
// be thrown.
BlockServiceUtils.checkForPendingTasks(journalVolume.getTenant().getURI(), Arrays.asList(journalVolume), _dbClient);
VirtualPool migrateToVpool = journalMigration.getMigrateToVpool();
logMigrations.append(String.format("\tRP+VPLEX migrate JOURNAL [%s](%s) to vpool [%s](%s)\n", journalVolume.getLabel(), journalVolume.getId(), migrateToVpool.getLabel(), migrateToVpool.getId()));
singleMigrations.put(journalVolume, migrateToVpool);
} else {
_log.info(String.format("No migration info was found for Journal volume [%s](%s). Skipping...", journalVolume.getLabel(), journalVolume.getId()));
}
}
}
}
}
use of com.emc.storageos.api.service.impl.resource.utils.RPVPlexMigration in project coprhd-controller by CoprHD.
the class RPBlockServiceApiImpl method findSourceAndTargetMigrations.
/**
* Determines if there are any Source or Target migrations and if so adds them to the
* containers passed in.
*
* @param volumes List of volumes to migrate
* @param newVpool The newVpool for Source migrations
* @param sourceMigrationsExist Determines if there are any source migrations
* @param allSourceVolumesToMigrate Container for all Source volumes to migrate
* @param targetMigrationsExist Determines if there are any Target migrations
* @param allTargetVolumesToMigrate Container for all Target volumes to migrate
* @param targetVpoolMigrations List of RPVPlexMigration for Target
*/
private void findSourceAndTargetMigrations(List<Volume> volumes, VirtualPool newVpool, boolean sourceMigrationsExist, HashMap<VirtualPool, List<Volume>> allSourceVolumesToMigrate, boolean targetMigrationsExist, HashMap<VirtualPool, List<Volume>> allTargetVolumesToMigrate, List<RPVPlexMigration> targetVpoolMigrations) {
for (Volume volume : volumes) {
if (sourceMigrationsExist) {
// Group Source migrations by new vpool to Source volumes
List<Volume> sourceVolumesToMigrate = allSourceVolumesToMigrate.get(newVpool);
if (sourceVolumesToMigrate == null) {
sourceVolumesToMigrate = new ArrayList<Volume>();
allSourceVolumesToMigrate.put(newVpool, sourceVolumesToMigrate);
}
sourceVolumesToMigrate.add(volume);
}
if (targetMigrationsExist) {
// Find the Targets for the volume
StringSet rpTargets = volume.getRpTargets();
for (String rpTargetId : rpTargets) {
Volume rpTargetVolume = _dbClient.queryObject(Volume.class, URI.create(rpTargetId));
// Check to see if this Target volume qualifies for migration
RPVPlexMigration targetMigration = null;
for (RPVPlexMigration migration : targetVpoolMigrations) {
if (rpTargetVolume.getVirtualArray().equals(migration.getVarray()) && rpTargetVolume.getVirtualPool().equals(migration.getMigrateFromVpool().getId())) {
targetMigration = migration;
break;
}
}
// a new task for the operation.
if (targetMigration != null) {
// Make sure the target volume is not involved in another task. If it is, an exception will
// be thrown.
BlockServiceUtils.checkForPendingTasks(rpTargetVolume.getTenant().getURI(), Arrays.asList(rpTargetVolume), _dbClient);
// Make sure the target volume does not have any other restrictions and is
// valid for migrations (ex: may have snapshots)
VirtualPool migrateFromVpool = targetMigration.getMigrateFromVpool();
VirtualPool migrateToVpool = targetMigration.getMigrateToVpool();
BlockService.verifyVPlexVolumeForDataMigration(rpTargetVolume, migrateFromVpool, migrateToVpool, _dbClient);
// Group Target migrations by new vpool to Target volumes
List<Volume> targetVolumesToMigrate = allTargetVolumesToMigrate.get(migrateToVpool);
if (targetVolumesToMigrate == null) {
targetVolumesToMigrate = new ArrayList<Volume>();
allTargetVolumesToMigrate.put(migrateToVpool, targetVolumesToMigrate);
}
targetVolumesToMigrate.add(rpTargetVolume);
} else {
_log.info(String.format("No migration info was found for Target volume [%s](%s). Skipping...", rpTargetVolume.getLabel(), rpTargetVolume.getId()));
}
}
}
}
}
use of com.emc.storageos.api.service.impl.resource.utils.RPVPlexMigration in project coprhd-controller by CoprHD.
the class RPBlockServiceApiImpl method rpVPlexDataMigration.
/**
* Create the RP+VPLEX/MetroPoint Data Migration volume descriptors to be passed to the block orchestration
* change vpool workflow.
*
* @param volumes The RP+VPLEX/MetroPoint volumes to migrate
* @param newVpool The vpool to migrate to
* @param taskId The task
* @param validMigrations All valid migrations
* @param vpoolChangeParam VirtualPool change parameters used to determine if need to suspend on migration
* @return List of tasks
* @throws InternalException
*/
private TaskList rpVPlexDataMigration(List<Volume> volumes, VirtualPool newVpool, String taskId, List<RPVPlexMigration> validMigrations, VirtualPoolChangeParam vpoolChangeParam) throws InternalException {
// TaskList to return
TaskList taskList = new TaskList();
if (validMigrations == null || validMigrations.isEmpty()) {
_log.warn(String.format("No RP+VPLEX migrations found"));
return taskList;
}
_log.info(String.format("%s RP+VPLEX migrations found", validMigrations.size()));
List<RPVPlexMigration> sourceVpoolMigrations = new ArrayList<RPVPlexMigration>();
List<RPVPlexMigration> targetVpoolMigrations = new ArrayList<RPVPlexMigration>();
List<RPVPlexMigration> journalVpoolMigrations = new ArrayList<RPVPlexMigration>();
try {
// Group the migrations by personality
for (RPVPlexMigration migration : validMigrations) {
switch(migration.getType()) {
case SOURCE:
sourceVpoolMigrations.add(migration);
break;
case TARGET:
targetVpoolMigrations.add(migration);
break;
case METADATA:
journalVpoolMigrations.add(migration);
break;
default:
break;
}
}
// Convenience booleans to quickly check which migrations are required
boolean sourceMigrationsExist = (!sourceVpoolMigrations.isEmpty());
boolean targetMigrationsExist = (!targetVpoolMigrations.isEmpty());
boolean journalMigrationsExist = (!journalVpoolMigrations.isEmpty());
if (!sourceMigrationsExist && (targetMigrationsExist || journalMigrationsExist)) {
// When there are no Source migrations and the Source volumes are in RGs we need
// to make sure all those Source volumes are in the request.
//
// Otherwise we could have the case where some Source volumes have been moved to a
// new vpool and some have not.
validateSourceVolumesInRGForMigrationRequest(volumes);
}
_log.info(String.format("%s SOURCE migrations, %s TARGET migrations, %s METADATA migrations", sourceVpoolMigrations.size(), targetVpoolMigrations.size(), journalVpoolMigrations.size()));
// Buffer to log all the migrations
StringBuffer logMigrations = new StringBuffer();
logMigrations.append("\n\nRP+VPLEX Migrations:\n");
// Step 2
//
// Let's find out if there are any Source and Target volumes to migrate.
// Source and Target migrations will be treated in two different ways depending
// on if the VPLEX backend volumes are in an array Replication Group(RG) or not.
//
// 1. In RG
// Being in an RG means that the all volumes in the RG will need to be
// grouped and migrated together.
// NOTE:
// a) All volumes in the RG will need to be selected for the operation to proceed.
// b) There is restriction on the number of volumes in the RG that will be allowed for the migration.
// Default value is 25 volumes. This is an existing limitation in the VPLEX code.
// c) Journal volumes will never be in a backend RG.
// 2. Not in RG
// Treated as a normal single migration.
HashMap<VirtualPool, List<Volume>> allSourceVolumesToMigrate = new HashMap<VirtualPool, List<Volume>>();
HashMap<VirtualPool, List<Volume>> allTargetVolumesToMigrate = new HashMap<VirtualPool, List<Volume>>();
findSourceAndTargetMigrations(volumes, newVpool, sourceMigrationsExist, allSourceVolumesToMigrate, targetMigrationsExist, allTargetVolumesToMigrate, targetVpoolMigrations);
// Step 3
//
// Handle all Source and Target migrations. The ones grouped by RG will
// be migrated together. The others will be treated as single migrations.
// Map to store single migrations (those not grouped by RG)
Map<Volume, VirtualPool> singleMigrations = new HashMap<Volume, VirtualPool>();
// Source
//
// Source volumes could need to be grouped by RG or not (single migration).
//
// Grouped migrations will have a migration WF initiated via the
// call to migrateVolumesInReplicationGroup().
//
// Single migrations will be collected afterwards to be migrated explicitly in Step 4 and 6
// below.
rpVPlexGroupedMigrations(allSourceVolumesToMigrate, singleMigrations, Volume.PersonalityTypes.SOURCE.name(), logMigrations, taskList, vpoolChangeParam);
// Targets
//
// Target volumes could need to be grouped by RG or not (single migration).
//
// Grouped migrations will have a migration WF initiated via the
// call to migrateVolumesInReplicationGroup().
//
// Single migrations will be collected afterwards to be migrated explicitly in Step 4 and 6
// below.
rpVPlexGroupedMigrations(allTargetVolumesToMigrate, singleMigrations, Volume.PersonalityTypes.TARGET.name(), logMigrations, taskList, vpoolChangeParam);
// Journals
//
// Journals will never be in RGs so they will always be treated as single migrations.
// Journal volumes must be checked against the CG. So we need to gather all affected
// CGs in the request.
// A new task will be generated to track each Journal migration.
Set<URI> cgURIs = BlockConsistencyGroupUtils.getAllCGsFromVolumes(volumes);
rpVPlexJournalMigrations(journalMigrationsExist, journalVpoolMigrations, singleMigrations, cgURIs, logMigrations);
logMigrations.append("\n");
_log.info(logMigrations.toString());
// Step 4
//
// Create the migration volume descriptors for all single migrations that are not in an RG.
List<VolumeDescriptor> migrateVolumeDescriptors = new ArrayList<VolumeDescriptor>();
for (Map.Entry<Volume, VirtualPool> entry : singleMigrations.entrySet()) {
Volume migrateVolume = entry.getKey();
VirtualPool migrateToVpool = entry.getValue();
boolean allowHighAvailabilityMigrations = true;
if (!migrateVolume.getAssociatedVolumes().isEmpty()) {
// This is mainly an issue for RP+VPLEX journals.
if (migrateVolume.getAssociatedVolumes().size() <= 1) {
allowHighAvailabilityMigrations = false;
}
} else {
// Ex: Active Source journals that use the default Source vpool for provisioning.
if (Volume.PersonalityTypes.METADATA.name().equals(migrateVolume.getPersonality())) {
allowHighAvailabilityMigrations = false;
}
}
StorageSystem vplexStorageSystem = _dbClient.queryObject(StorageSystem.class, migrateVolume.getStorageController());
migrateVolumeDescriptors.addAll(vplexBlockServiceApiImpl.createChangeVirtualPoolDescriptors(vplexStorageSystem, migrateVolume, migrateToVpool, taskId, null, null, null, allowHighAvailabilityMigrations));
}
// ensure the task is completed correctly and the vpools updated by the completer.
if (!sourceMigrationsExist && (targetMigrationsExist || journalMigrationsExist)) {
_log.info("No RP+VPLEX Source migrations detected, creating DUMMY_MIGRATE volume descriptors for the Source volumes.");
for (Volume volume : volumes) {
if (volume.checkPersonality(Volume.PersonalityTypes.SOURCE)) {
// Add the VPLEX Virtual Volume Descriptor for change vpool
VolumeDescriptor dummyMigrate = new VolumeDescriptor(VolumeDescriptor.Type.DUMMY_MIGRATE, volume.getStorageController(), volume.getId(), volume.getPool(), null);
Map<String, Object> volumeParams = new HashMap<String, Object>();
volumeParams.put(VolumeDescriptor.PARAM_VPOOL_CHANGE_EXISTING_VOLUME_ID, volume.getId());
volumeParams.put(VolumeDescriptor.PARAM_VPOOL_CHANGE_NEW_VPOOL_ID, newVpool.getId());
volumeParams.put(VolumeDescriptor.PARAM_VPOOL_CHANGE_OLD_VPOOL_ID, volume.getVirtualPool());
dummyMigrate.setParameters(volumeParams);
migrateVolumeDescriptors.add(dummyMigrate);
}
}
}
// single migrations.
if (!migrateVolumeDescriptors.isEmpty()) {
// Generate the correct task information for single migrations
List<Volume> migrationVolumes = new ArrayList<Volume>();
migrationVolumes.addAll(singleMigrations.keySet());
taskList.getTaskList().addAll(createTasksForVolumes(newVpool, migrationVolumes, taskId).getTaskList());
// Invoke the block orchestrator for the change vpool operation
BlockOrchestrationController controller = getController(BlockOrchestrationController.class, BlockOrchestrationController.BLOCK_ORCHESTRATION_DEVICE);
controller.changeVirtualPool(migrateVolumeDescriptors, taskId);
} else {
_log.info(String.format("No extra migrations needed."));
}
} catch (Exception e) {
String errorMsg = String.format("Volume VirtualPool change error: %s", e.getMessage());
_log.error(errorMsg, e);
for (TaskResourceRep volumeTask : taskList.getTaskList()) {
volumeTask.setState(Operation.Status.error.name());
volumeTask.setMessage(errorMsg);
_dbClient.updateTaskOpStatus(Volume.class, volumeTask.getResource().getId(), taskId, new Operation(Operation.Status.error.name(), errorMsg));
}
throw e;
}
return taskList;
}
Aggregations