added online/offline copy method for Primera storage adapter (#11298)

This commit is contained in:
shrikantjoshi-hpe 2025-08-03 12:31:37 +05:30 committed by GitHub
parent 4a4b5a5e5f
commit 5cac4f6c44
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 68 additions and 10 deletions

View File

@ -87,8 +87,9 @@ public interface ProviderAdapter {
/**
* Copy a source object to a destination volume. The source object can be a Volume, Snapshot, or Template
* @param newSize the desired size in bytes for the destination volume (supports resize-during-copy)
*/
public ProviderVolume copy(ProviderAdapterContext context, ProviderAdapterDataObject sourceVolume, ProviderAdapterDataObject targetVolume);
public ProviderVolume copy(ProviderAdapterContext context, ProviderAdapterDataObject sourceVolume, ProviderAdapterDataObject targetVolume, Long newSize);
/**
* Make a device-specific snapshot of the provided volume

View File

@ -337,7 +337,8 @@ public class AdaptiveDataStoreDriverImpl extends CloudStackPrimaryDataStoreDrive
ProviderAdapterDataObject sourceIn = newManagedDataObject(srcdata, storagePool);
ProviderAdapterDataObject destIn = newManagedDataObject(destdata, storagePool);
outVolume = api.copy(context, sourceIn, destIn);
// Call provider adapter copy method with destination size parameter for resize-during-copy support
outVolume = api.copy(context, sourceIn, destIn, destdata.getSize());
// populate this data - it may be needed later
destIn.setExternalName(outVolume.getExternalName());

View File

@ -367,7 +367,8 @@ public class FlashArrayAdapter implements ProviderAdapter {
@Override
public ProviderVolume copy(ProviderAdapterContext context, ProviderAdapterDataObject sourceDataObject,
ProviderAdapterDataObject destDataObject) {
ProviderAdapterDataObject destDataObject, Long newSize) {
// Add new parameter as newSize to match method declaration but not used anywhere
// private ManagedVolume copy(ManagedVolume sourceVolume, String destNamespace,
// String destName) {
if (sourceDataObject == null || sourceDataObject.getExternalName() == null

View File

@ -288,15 +288,22 @@ public class PrimeraAdapter implements ProviderAdapter {
@Override
public ProviderVolume copy(ProviderAdapterContext context, ProviderAdapterDataObject sourceVolumeInfo,
ProviderAdapterDataObject targetVolumeInfo) {
ProviderAdapterDataObject targetVolumeInfo, Long newSize) {
// Log the start of the copy operation with source volume details
logger.debug("PrimeraAdapter: Starting volume copy operation - source volume: '{}', target volume: '{}', requested new size: {} bytes ({} MiB)",
sourceVolumeInfo.getExternalName(), targetVolumeInfo.getName(), newSize, newSize / PrimeraAdapter.BYTES_IN_MiB);
// Flag to determine copy method: online copy (direct clone) vs offline copy (with resize)
boolean onlineCopy = true;
PrimeraVolumeCopyRequest request = new PrimeraVolumeCopyRequest();
PrimeraVolumeCopyRequestParameters parms = new PrimeraVolumeCopyRequestParameters();
assert sourceVolumeInfo.getExternalName() != null: "External provider name not provided on copy request to Primera volume provider";
// if we have no external name, treat it as a new volume
// Generate external name for target volume if not already set
if (targetVolumeInfo.getExternalName() == null) {
targetVolumeInfo.setExternalName(ProviderVolumeNamer.generateObjectName(context, targetVolumeInfo));
logger.debug("PrimeraAdapter: Generated external name '{}' for target volume", targetVolumeInfo.getExternalName());
}
ProviderVolume sourceVolume = this.getVolume(context, sourceVolumeInfo);
@ -304,23 +311,71 @@ public class PrimeraAdapter implements ProviderAdapter {
throw new RuntimeException("Source volume " + sourceVolumeInfo.getExternalUuid() + " with provider name " + sourceVolumeInfo.getExternalName() + " not found on storage provider");
}
// Determine copy method based on size difference
// Online copy: Direct clone without size change (faster, immediate)
// Offline copy: Copy with potential resize (slower, requires task completion wait)
Long sourceSize = sourceVolume.getAllocatedSizeInBytes();
if (newSize == null || sourceSize == null || !newSize.equals(sourceSize)) {
logger.debug("PrimeraAdapter: Volume size change detected (source: {} bytes, target: {} bytes) - using offline copy method",
sourceSize, newSize);
onlineCopy = false;
} else {
logger.debug("PrimeraAdapter: No size change required (both {} bytes) - using online copy method for faster cloning", newSize);
}
// Check if target volume already exists on the storage provider
ProviderVolume targetVolume = this.getVolume(context, targetVolumeInfo);
if (targetVolume == null) {
if (!onlineCopy) {
// For offline copy, pre-create the target volume with the desired size
logger.debug("PrimeraAdapter: Offline copy mode - pre-creating target volume '{}' with size {} bytes",
targetVolumeInfo.getName(), sourceVolume.getAllocatedSizeInBytes());
this.create(context, targetVolumeInfo, null, sourceVolume.getAllocatedSizeInBytes());
} else {
// For online copy, the target volume will be created automatically during the clone operation
logger.debug("PrimeraAdapter: Online copy mode - target volume '{}' will be created automatically during clone operation",
targetVolumeInfo.getName());
}
} else {
logger.warn("PrimeraAdapter: Target volume '{}' already exists on storage provider - proceeding with copy operation",
targetVolumeInfo.getExternalName());
}
parms.setDestVolume(targetVolumeInfo.getExternalName());
if (onlineCopy) {
// Online copy configuration: immediate clone with deduplication and compression
parms.setOnline(true);
parms.setDestCPG(cpg);
parms.setTpvv(false);
parms.setReduce(true);
logger.debug("PrimeraAdapter: Configuring online copy - destination CPG: '{}', deduplication enabled, thin provisioning disabled", cpg);
} else {
// Offline copy configuration: background task with high priority
parms.setOnline(false);
parms.setPriority(1);
parms.setPriority(1); // Set high priority for faster completion
logger.debug("PrimeraAdapter: Configuring offline copy with high priority for target volume '{}'", targetVolumeInfo.getName());
}
// Set request parameters and initiate the copy operation
request.setParameters(parms);
PrimeraTaskReference taskref = POST("/volumes/" + sourceVolumeInfo.getExternalName(), request, new TypeReference<PrimeraTaskReference>() {});
if (taskref == null) {
logger.error("PrimeraAdapter: Failed to initiate copy operation - no task reference returned from storage provider");
throw new RuntimeException("Unable to retrieve task used to copy to newly created volume");
}
// Handle task completion based on copy method
if (!onlineCopy) {
// Offline copy requires waiting for task completion
logger.debug("PrimeraAdapter: Offline copy initiated - waiting for task completion (TaskID: {})", taskref.getTaskid());
waitForTaskToComplete(taskref.getTaskid(), "copy volume " + sourceVolumeInfo.getExternalName() + " to " +
targetVolumeInfo.getExternalName(), taskWaitTimeoutMs);
logger.debug("PrimeraAdapter: Offline copy operation completed successfully");
} else {
// Online copy completes immediately
logger.debug("PrimeraAdapter: Online copy operation completed successfully (TaskID: {})", taskref.getTaskid());
}
return this.getVolume(context, targetVolumeInfo);
}