Skip to content

Commit ae96e9b

Browse files
CSTACKEX-18_2: delete snapshot should be done over plugin path not on agent path
1 parent 79730ed commit ae96e9b

File tree

1 file changed

+79
-24
lines changed

1 file changed

+79
-24
lines changed

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,13 @@
4343
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
4444
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
4545
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
46-
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
47-
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
4846
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
4947
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
50-
import org.apache.cloudstack.engine.subsystem.api.storage.StorageAction;
5148
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
5249
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
5350
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
5451
import org.apache.cloudstack.storage.command.CommandResult;
5552
import org.apache.cloudstack.storage.command.CreateObjectAnswer;
56-
import org.apache.cloudstack.storage.command.DeleteCommand;
5753
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
5854
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
5955
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
@@ -94,7 +90,6 @@ public class OntapPrimaryDatastoreDriver implements PrimaryDataStoreDriver {
9490
@Inject private VolumeDao volumeDao;
9591
@Inject private VolumeDetailsDao volumeDetailsDao;
9692
@Inject private SnapshotDetailsDao snapshotDetailsDao;
97-
@Inject private EndPointSelector epSelector;
9893

9994
@Override
10095
public Map<String, String> getCapabilities() {
@@ -239,8 +234,7 @@ private CloudStackVolume createCloudStackVolume(DataStore dataStore, DataObject
239234
* Deletes a volume or snapshot from the ONTAP storage system.
240235
*
241236
* <p>For volumes, deletes the backend storage object (LUN for iSCSI, no-op for NFS).
242-
* For snapshots, delegates to the hypervisor agent to delete the QCOW2 snapshot
243-
* from the NFS-mounted ONTAP volume.</p>
237+
* For snapshots, deletes the FlexVolume snapshot from ONTAP that was created by takeSnapshot.</p>
244238
*/
245239
@Override
246240
public void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallback<CommandResult> callback) {
@@ -266,23 +260,8 @@ public void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallbac
266260
commandResult.setResult(null);
267261
commandResult.setSuccess(true);
268262
} else if (data.getType() == DataObjectType.SNAPSHOT) {
269-
// Delegate snapshot deletion to the hypervisor agent, same as
270-
// CloudStackPrimaryDataStoreDriverImpl. The KVM host deletes the
271-
// QCOW2 snapshot file from the NFS-mounted ONTAP volume.
272-
DeleteCommand cmd = new DeleteCommand(data.getTO());
273-
EndPoint ep = epSelector.select(data, StorageAction.DELETESNAPSHOT);
274-
if (ep == null) {
275-
String errMsg = "No remote endpoint to send DeleteCommand for snapshot, check if host or ssvm is down?";
276-
s_logger.error(errMsg);
277-
commandResult.setResult(errMsg);
278-
} else {
279-
Answer answer = ep.sendMessage(cmd);
280-
if (answer != null && !answer.getResult()) {
281-
commandResult.setResult(answer.getDetails());
282-
} else {
283-
commandResult.setSuccess(true);
284-
}
285-
}
263+
// Delete the ONTAP FlexVolume snapshot that was created by takeSnapshot
264+
deleteOntapSnapshot((SnapshotInfo) data, commandResult);
286265
} else {
287266
throw new CloudRuntimeException("deleteAsync: Unsupported data object type: " + data.getType());
288267
}
@@ -295,6 +274,82 @@ public void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallbac
295274
}
296275
}
297276

277+
/**
278+
* Deletes an ONTAP FlexVolume snapshot.
279+
*
280+
* <p>Retrieves the snapshot details stored during takeSnapshot and calls the ONTAP
281+
* REST API to delete the FlexVolume snapshot.</p>
282+
*
283+
* @param snapshotInfo The CloudStack snapshot to delete
284+
* @param commandResult Result object to populate with success/failure
285+
*/
286+
private void deleteOntapSnapshot(SnapshotInfo snapshotInfo, CommandResult commandResult) {
287+
long snapshotId = snapshotInfo.getId();
288+
s_logger.info("deleteOntapSnapshot: Deleting ONTAP FlexVolume snapshot for CloudStack snapshot [{}]", snapshotId);
289+
290+
try {
291+
// Retrieve snapshot details stored during takeSnapshot
292+
String flexVolUuid = getSnapshotDetail(snapshotId, Constants.BASE_ONTAP_FV_ID);
293+
String ontapSnapshotUuid = getSnapshotDetail(snapshotId, Constants.ONTAP_SNAP_ID);
294+
String snapshotName = getSnapshotDetail(snapshotId, Constants.ONTAP_SNAP_NAME);
295+
String poolIdStr = getSnapshotDetail(snapshotId, Constants.PRIMARY_POOL_ID);
296+
297+
if (flexVolUuid == null || ontapSnapshotUuid == null) {
298+
s_logger.warn("deleteOntapSnapshot: Missing ONTAP snapshot details for snapshot [{}]. " +
299+
"flexVolUuid={}, ontapSnapshotUuid={}. Snapshot may have been created by a different method or already deleted.",
300+
snapshotId, flexVolUuid, ontapSnapshotUuid);
301+
// Consider this a success since there's nothing to delete on ONTAP
302+
commandResult.setSuccess(true);
303+
commandResult.setResult(null);
304+
return;
305+
}
306+
307+
long poolId = Long.parseLong(poolIdStr);
308+
Map<String, String> poolDetails = storagePoolDetailsDao.listDetailsKeyPairs(poolId);
309+
310+
StorageStrategy storageStrategy = Utility.getStrategyByStoragePoolDetails(poolDetails);
311+
SnapshotFeignClient snapshotClient = storageStrategy.getSnapshotFeignClient();
312+
String authHeader = storageStrategy.getAuthHeader();
313+
314+
s_logger.info("deleteOntapSnapshot: Deleting ONTAP snapshot [{}] (uuid={}) from FlexVol [{}]",
315+
snapshotName, ontapSnapshotUuid, flexVolUuid);
316+
317+
// Call ONTAP REST API to delete the snapshot
318+
JobResponse jobResponse = snapshotClient.deleteSnapshot(authHeader, flexVolUuid, ontapSnapshotUuid);
319+
320+
if (jobResponse != null && jobResponse.getJob() != null) {
321+
// Poll for job completion
322+
Boolean jobSucceeded = storageStrategy.jobPollForSuccess(jobResponse.getJob().getUuid(), 30, 2);
323+
if (!jobSucceeded) {
324+
throw new CloudRuntimeException("deleteOntapSnapshot: Delete job failed for snapshot [" +
325+
snapshotName + "] on FlexVol [" + flexVolUuid + "]");
326+
}
327+
}
328+
329+
s_logger.info("deleteOntapSnapshot: Successfully deleted ONTAP snapshot [{}] (uuid={}) for CloudStack snapshot [{}]",
330+
snapshotName, ontapSnapshotUuid, snapshotId);
331+
332+
commandResult.setSuccess(true);
333+
commandResult.setResult(null);
334+
335+
} catch (Exception e) {
336+
// Check if the error indicates snapshot doesn't exist (already deleted)
337+
String errorMsg = e.getMessage();
338+
if (errorMsg != null && (errorMsg.contains("404") || errorMsg.contains("not found") ||
339+
errorMsg.contains("does not exist"))) {
340+
s_logger.warn("deleteOntapSnapshot: ONTAP snapshot for CloudStack snapshot [{}] not found, " +
341+
"may have been already deleted. Treating as success.", snapshotId);
342+
commandResult.setSuccess(true);
343+
commandResult.setResult(null);
344+
} else {
345+
s_logger.error("deleteOntapSnapshot: Failed to delete ONTAP snapshot for CloudStack snapshot [{}]: {}",
346+
snapshotId, e.getMessage(), e);
347+
commandResult.setSuccess(false);
348+
commandResult.setResult(e.getMessage());
349+
}
350+
}
351+
}
352+
298353
@Override
299354
public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
300355
}

0 commit comments

Comments
 (0)