diff --git a/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java b/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java
index bd35fc4f9112..2bc06c12ca6d 100644
--- a/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java
+++ b/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java
@@ -204,7 +204,8 @@ public void createAsync(DataStore dataStore, DataObject dataObject, AsyncComplet
* - DATA or ROOT snapshot → new attachable data volume (ROOT is never bootable
* via this path; bootable recovery is createTemplate(snapshotid) → deploy).
* - Backend: iSCSI → POST /api/storage/luns (clone.source in .snapshot/);
- * NFS → POST /api/storage/file/clone with snapshot.name.
+ * NFS → POST /api/storage/file/clone with source_path
+ * ./.snapshot//.
* - IOPS: the new volume's min/max (snapshot values, or the root offering when one
* is supplied). Applied here; the temporary template clone does not get a policy.
*
@@ -517,7 +518,8 @@ private Long getSnapshotIdForCloning(long volumeId) {
* {@code createTemplate(snapshotid)} then deploy.
* iSCSI — {@code POST /api/storage/luns} with
* {@code clone.source.name=/vol/<fv>/.snapshot/<snap>/<lun>}
- * NFS3 — {@code POST /api/storage/file/clone} with {@code snapshot.name}
+ * NFS3 — {@code POST /api/storage/file/clone} with
+ * {@code source_path=./.snapshot/<snapshot-name>/<source-file>}
* Not this path — in-place revert ({@code revertSnapshot}); VM/instance snapshots;
* secondary-storage archive restore.
*
@@ -2390,14 +2392,17 @@ private CloudStackVolume createCloneFileFromSnapshotRequest(StoragePoolVO storag
String flexVolName = details.get(OntapStorageConstants.VOLUME_NAME);
FileInfo file = new FileInfo();
- file.setPath(OntapStorageUtils.toFlexVolRelativePath(sourceVolumePath, flexVolName));
+ // FlexVol-relative source path, for example "".
+ String relativeSourcePath = OntapStorageUtils.toFlexVolRelativePath(sourceVolumePath, flexVolName);
+ // Path assigned to file.setPath(), for example "./.snapshot//".
+ file.setPath(OntapStorageConstants.DOT + OntapStorageConstants.SNAPSHOT_PATH_SEGMENT + snapshotName
+ + OntapStorageConstants.SLASH + relativeSourcePath);
CloudStackVolume request = new CloudStackVolume();
request.setDatastoreId(String.valueOf(storagePool.getId()));
request.setVolumeInfo(volumeInfo);
request.setFile(file);
request.setDestinationPath(OntapStorageUtils.toFlexVolRelativePath(volumeInfo.getUuid(), flexVolName));
- request.setSnapshotName(snapshotName);
return request;
}
diff --git a/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/model/FileCloneRequest.java b/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/model/FileCloneRequest.java
index cefff9fd282f..a9f2a106e9a8 100644
--- a/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/model/FileCloneRequest.java
+++ b/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/model/FileCloneRequest.java
@@ -46,15 +46,6 @@ public class FileCloneRequest {
@JsonProperty("overwrite_destination")
private Boolean overwriteDestination;
- /**
- * Optional FlexVolume snapshot to clone from. When set, ONTAP clones {@code source_path}
- * as it existed in that snapshot rather than from the live file/LUN.
- *
- * Used by create-volume-from-snapshot (same FlexVol). Omitted for live template-cache clones.
- */
- @JsonProperty("snapshot")
- private SnapshotRef snapshot;
-
public FileCloneRequest() {
}
@@ -64,14 +55,6 @@ public FileCloneRequest(String flexVolUuid, String flexVolName, String sourcePat
this.destinationPath = destinationPath;
}
- public FileCloneRequest(String flexVolUuid, String flexVolName, String sourcePath, String destinationPath,
- String snapshotName) {
- this(flexVolUuid, flexVolName, sourcePath, destinationPath);
- if (snapshotName != null && !snapshotName.isEmpty()) {
- this.snapshot = new SnapshotRef(snapshotName);
- }
- }
-
public VolumeRef getVolume() {
return volume;
}
@@ -104,14 +87,6 @@ public void setOverwriteDestination(Boolean overwriteDestination) {
this.overwriteDestination = overwriteDestination;
}
- public SnapshotRef getSnapshot() {
- return snapshot;
- }
-
- public void setSnapshot(SnapshotRef snapshot) {
- this.snapshot = snapshot;
- }
-
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class VolumeRef {
@@ -147,37 +122,10 @@ public void setName(String name) {
}
}
- /**
- * Snapshot identity for {@code POST /api/storage/file/clone} when cloning from a FlexVol snapshot.
- */
- @JsonIgnoreProperties(ignoreUnknown = true)
- @JsonInclude(JsonInclude.Include.NON_NULL)
- public static class SnapshotRef {
-
- @JsonProperty("name")
- private String name;
-
- public SnapshotRef() {
- }
-
- public SnapshotRef(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
- }
-
@Override
public String toString() {
return "FileCloneRequest{volume=" + (volume != null ? volume.getUuid() : null)
+ ", sourcePath=" + sourcePath
- + ", destinationPath=" + destinationPath
- + ", snapshot=" + (snapshot != null ? snapshot.getName() : null) + "}";
+ + ", destinationPath=" + destinationPath + "}";
}
}
diff --git a/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/StorageStrategy.java b/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/StorageStrategy.java
index 5991ad7aef09..a19a90c59641 100644
--- a/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/StorageStrategy.java
+++ b/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/StorageStrategy.java
@@ -934,8 +934,9 @@ abstract public CloudStackVolume createTemplateCache(StoragePoolVO storagePool,
*
* - SAN — {@code lun.clone.source.name} set to
* {@code /vol/<fv>/.snapshot/<snap>/<lun>}
- * - NAS — {@link CloudStackVolume#getSnapshotName()} set; it is sent as
- * {@code snapshot.name} on {@code POST /api/storage/file/clone}
+ * - NAS — {@code file.path} set to
+ * {@code ./.snapshot/<snapshot-name>/<source-file>} and sent as
+ * {@code source_path} on {@code POST /api/storage/file/clone}
*
*
* @param cloudstackVolume describes the clone to create; the source is carried in the
diff --git a/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/UnifiedNASStrategy.java b/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/UnifiedNASStrategy.java
index 3025727d8e78..7973412feb5e 100644
--- a/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/UnifiedNASStrategy.java
+++ b/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/UnifiedNASStrategy.java
@@ -181,8 +181,7 @@ public void deleteCloudStackVolume(CloudStackVolume cloudstackVolume) {
* Clones a file inside the FlexVolume using ONTAP's file clone API.
*
* The source is taken from {@code file.path} and the destination from
- * {@code destinationPath}, both relative to the root of the FlexVolume backing the pool.
- * When {@code snapshotName} is set, the source is read from that FlexVolume snapshot.
+ * {@code destinationPath}, both relative to the root of the FlexVolume backing the pool.
*/
@Override
public CloudStackVolume cloneCloudStackVolume(CloudStackVolume cloudstackVolume) {
@@ -203,12 +202,11 @@ public CloudStackVolume cloneCloudStackVolume(CloudStackVolume cloudstackVolume)
}
String sourcePath = cloudstackVolume.getFile().getPath();
String destinationPath = cloudstackVolume.getDestinationPath();
- String snapshotName = cloudstackVolume.getSnapshotName();
- logger.info("cloneCloudStackVolume: Cloning file [{}] to [{}] in FlexVol [{}] from snapshot [{}]",
- sourcePath, destinationPath, flexVolName, snapshotName);
+ logger.info("cloneCloudStackVolume: Cloning file [{}] to [{}] in FlexVol [{}]",
+ sourcePath, destinationPath, flexVolName);
try {
- FileCloneRequest request = new FileCloneRequest(flexVolUuid, flexVolName, sourcePath, destinationPath, snapshotName);
+ FileCloneRequest request = new FileCloneRequest(flexVolUuid, flexVolName, sourcePath, destinationPath);
JobResponse jobResponse = nasFeignClient.cloneFile(getAuthHeader(), request);
pollJobIfPresent(jobResponse, "clone file [" + sourcePath + "] to [" + destinationPath + "]");
@@ -221,7 +219,6 @@ public CloudStackVolume cloneCloudStackVolume(CloudStackVolume cloudstackVolume)
clonedCloudStackVolume.setFile(clonedFile);
clonedCloudStackVolume.setDatastoreId(cloudstackVolume.getDatastoreId());
clonedCloudStackVolume.setVolumeInfo(cloudstackVolume.getVolumeInfo());
- clonedCloudStackVolume.setSnapshotName(snapshotName);
return clonedCloudStackVolume;
} catch (FeignException e) {
logger.error("FeignException occurred while cloning file [{}], Status: {}, Exception: {}",
diff --git a/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/model/CloudStackVolume.java b/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/model/CloudStackVolume.java
index 5147e9ff7ac2..ab38e3045f51 100644
--- a/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/model/CloudStackVolume.java
+++ b/plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/model/CloudStackVolume.java
@@ -51,12 +51,6 @@ public class CloudStackVolume {
*/
private String destinationPath;
- /**
- * ONTAP FlexVolume snapshot name when cloning a new file/LUN from a snapshot
- * (create-volume-from-snapshot). Null for live clones (e.g. template cache).
- */
- private String snapshotName;
-
private DataObject volumeInfo; // This is needed as we need DataObject to be passed to agent to create volume
public FileInfo getFile() {
@@ -107,12 +101,4 @@ public void setDestinationPath(String destinationPath) {
this.destinationPath = destinationPath;
}
- public String getSnapshotName() {
- return snapshotName;
- }
-
- public void setSnapshotName(String snapshotName) {
- this.snapshotName = snapshotName;
- }
-
}
diff --git a/plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriverTest.java b/plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriverTest.java
index ec55c72b9ccc..b675618ae2a8 100644
--- a/plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriverTest.java
+++ b/plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriverTest.java
@@ -1236,9 +1236,8 @@ void testCreateAsync_VolumeClonedFromSnapshot_NfsSuccess() {
ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(CloudStackVolume.class);
verify(nasStrategy).cloneCloudStackVolume(requestCaptor.capture());
CloudStackVolume request = requestCaptor.getValue();
- assertEquals("source-file-uuid", request.getFile().getPath());
+ assertEquals("./.snapshot/snap_cs200/source-file-uuid", request.getFile().getPath());
assertEquals("new-volume-uuid", request.getDestinationPath());
- assertEquals("snap_cs200", request.getSnapshotName());
assertEquals("1", request.getDatastoreId());
verify(nasStrategy, never()).createCloudStackVolume(any());
}
@@ -3461,9 +3460,9 @@ void testCopyAsync_NfsTemporarySnapshotCopy_SetsFilePath() {
ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(CloudStackVolume.class);
verify(nasStrategy).cloneCloudStackVolume(requestCaptor.capture());
- assertEquals("source-file-uuid", requestCaptor.getValue().getFile().getPath());
+ assertEquals("./.snapshot/snap_cs200/source-file-uuid",
+ requestCaptor.getValue().getFile().getPath());
assertEquals("tmp-volume-uuid", requestCaptor.getValue().getDestinationPath());
- assertEquals("snap_cs200", requestCaptor.getValue().getSnapshotName());
assertEquals("tmp-volume-uuid", dbVolume.getPath());
assertEquals(Storage.ImageFormat.QCOW2, dbVolume.getFormat());
diff --git a/plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/service/UnifiedNASStrategyTest.java b/plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/service/UnifiedNASStrategyTest.java
index 315ee7ed2127..60bdf64855e3 100755
--- a/plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/service/UnifiedNASStrategyTest.java
+++ b/plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/service/UnifiedNASStrategyTest.java
@@ -1176,11 +1176,10 @@ public void testCloneCloudStackVolume_Success() {
assertEquals("volume-uuid", captor.getValue().getDestinationPath());
assertEquals("flexvol1", captor.getValue().getVolume().getName());
assertEquals("flexvol-uuid-1", captor.getValue().getVolume().getUuid());
- assertNull(captor.getValue().getSnapshot());
}
@Test
- public void testCloneCloudStackVolume_SnapshotSource_SendsSnapshotName() {
+ public void testCloneCloudStackVolume_SnapshotSource_UsesSnapshotSourcePath() {
VolumeObject volumeObject = mock(VolumeObject.class);
VolumeVO volumeVO = mock(VolumeVO.class);
when(volumeObject.getId()).thenReturn(100L);
@@ -1193,26 +1192,22 @@ public void testCloneCloudStackVolume_SnapshotSource_SendsSnapshotName() {
when(storagePoolDetailsDao.listDetailsKeyPairs(1L)).thenReturn(details);
FileInfo source = new FileInfo();
- source.setPath("source-file-uuid");
+ source.setPath("./.snapshot/snap_cs200/source-file-uuid");
CloudStackVolume request = new CloudStackVolume();
request.setDatastoreId("1");
request.setVolumeInfo(volumeObject);
request.setFile(source);
request.setDestinationPath("new-volume-uuid");
- request.setSnapshotName("snap_cs200");
when(nasFeignClient.cloneFile(anyString(), any(FileCloneRequest.class))).thenReturn(new JobResponse());
CloudStackVolume result = strategy.cloneCloudStackVolume(request);
assertEquals("new-volume-uuid", result.getFile().getPath());
- assertEquals("snap_cs200", result.getSnapshotName());
ArgumentCaptor captor = ArgumentCaptor.forClass(FileCloneRequest.class);
verify(nasFeignClient).cloneFile(anyString(), captor.capture());
- assertEquals("source-file-uuid", captor.getValue().getSourcePath());
+ assertEquals("./.snapshot/snap_cs200/source-file-uuid", captor.getValue().getSourcePath());
assertEquals("new-volume-uuid", captor.getValue().getDestinationPath());
- assertNotNull(captor.getValue().getSnapshot());
- assertEquals("snap_cs200", captor.getValue().getSnapshot().getName());
}
@Test