Skip to content

Commit 3f3bcca

Browse files
CSTACKEX-306: cloned cloudstack volume should be deleted if subseuqent resize fails
1 parent 750ac0b commit 3f3bcca

2 files changed

Lines changed: 56 additions & 2 deletions

File tree

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,8 @@ private CloudStackVolume cloneCloudStackVolumeFromTemplate(StoragePoolVO storage
354354
if (requestedSize > templatePoolRef.getTemplateSize()) {
355355
logger.info("cloneCloudStackVolumeFromTemplate: Growing clone of template [{}] from {} to {} bytes for volume [{}]",
356356
templateId, templatePoolRef.getTemplateSize(), requestedSize, volumeInfo.getId());
357-
storageStrategy.resizeCloudStackVolume(cloned, requestedSize);
357+
resizeClonedVolumeOrCleanup(storageStrategy, cloned, requestedSize,
358+
"cloneCloudStackVolumeFromTemplate template [" + templateId + "] volume [" + volumeInfo.getId() + "]");
358359
}
359360

360361
return cloned;
@@ -449,12 +450,38 @@ private CloudStackVolume cloneCloudStackVolumeFromSnapshot(StoragePoolVO storage
449450
if (snapshotSize > 0 && requestedSize > snapshotSize) {
450451
logger.info("cloneCloudStackVolumeFromSnapshot: Growing clone of snapshot [{}] from {} to {} bytes for volume [{}]",
451452
csSnapshotId, snapshotSize, requestedSize, volumeInfo.getId());
452-
storageStrategy.resizeCloudStackVolume(cloned, requestedSize);
453+
resizeClonedVolumeOrCleanup(storageStrategy, cloned, requestedSize,
454+
"cloneCloudStackVolumeFromSnapshot snapshot [" + csSnapshotId + "] volume [" + volumeInfo.getId() + "]");
453455
}
454456

455457
return cloned;
456458
}
457459

460+
/**
461+
* Grows a just-created ONTAP clone to the requested size. If resize fails, deletes the clone
462+
* so the CloudStack create-failure path does not leave an orphaned LUN/file on the array.
463+
*/
464+
private void resizeClonedVolumeOrCleanup(StorageStrategy storageStrategy, CloudStackVolume cloned,
465+
long requestedSize, String context) {
466+
try {
467+
storageStrategy.resizeCloudStackVolume(cloned, requestedSize);
468+
} catch (RuntimeException resizeEx) {
469+
logger.error("{}: Resize to {} bytes failed; deleting orphaned ONTAP clone: {}",
470+
context, requestedSize, resizeEx.getMessage());
471+
bestEffortDeleteOrphanedClone(storageStrategy, cloned);
472+
throw resizeEx;
473+
}
474+
}
475+
476+
private void bestEffortDeleteOrphanedClone(StorageStrategy storageStrategy, CloudStackVolume cloned) {
477+
try {
478+
storageStrategy.deleteCloudStackVolume(cloned);
479+
logger.info("Deleted orphaned ONTAP clone after resize failure");
480+
} catch (Exception cleanupEx) {
481+
logger.warn("Failed to delete orphaned ONTAP clone after resize failure: {}", cleanupEx.getMessage());
482+
}
483+
}
484+
458485
private String requireSnapshotDetail(long csSnapshotId, String key) {
459486
String value = getSnapshotDetail(csSnapshotId, key);
460487
if (value == null || value.isEmpty()) {

‎plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriverTest.java‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,33 @@ void testCreateAsync_VolumeClonedFromSnapshot_GrowsWhenOfferingIsLarger() {
10251025
}
10261026
}
10271027

1028+
@Test
1029+
void testCreateAsync_VolumeClonedFromSnapshot_ResizeFails_DeletesOrphanedClone() {
1030+
stubVolumeCloneFromSnapshot(5368709120L, 21474836480L, ProtocolType.ISCSI.name());
1031+
1032+
Lun clonedLun = new Lun();
1033+
clonedLun.setName("/vol/vol1/test_volume");
1034+
clonedLun.setUuid("snap-cloned-lun-uuid");
1035+
CloudStackVolume cloned = new CloudStackVolume();
1036+
cloned.setLun(clonedLun);
1037+
1038+
try (MockedStatic<OntapStorageUtils> utilityMock = mockStatic(OntapStorageUtils.class, CALLS_REAL_METHODS)) {
1039+
utilityMock.when(() -> OntapStorageUtils.getStrategyByStoragePoolDetails(any())).thenReturn(sanStrategy);
1040+
when(sanStrategy.cloneCloudStackVolume(any())).thenReturn(cloned);
1041+
doThrow(new CloudRuntimeException("resize failed"))
1042+
.when(sanStrategy).resizeCloudStackVolume(eq(cloned), eq(21474836480L));
1043+
doNothing().when(sanStrategy).deleteCloudStackVolume(eq(cloned));
1044+
1045+
driver.createAsync(dataStore, volumeInfo, createCallback);
1046+
1047+
ArgumentCaptor<CreateCmdResult> resultCaptor = ArgumentCaptor.forClass(CreateCmdResult.class);
1048+
verify(createCallback).complete(resultCaptor.capture());
1049+
assertFalse(resultCaptor.getValue().isSuccess());
1050+
verify(sanStrategy).deleteCloudStackVolume(eq(cloned));
1051+
verify(volumeDetailsDao, never()).addDetail(eq(100L), eq(OntapStorageConstants.LUN_DOT_UUID), anyString(), anyBoolean());
1052+
}
1053+
}
1054+
10281055
@Test
10291056
void testCreateAsync_VolumeClonedFromSnapshot_NfsSuccess() {
10301057
stubVolumeCloneFromSnapshot(5368709120L, 5368709120L, ProtocolType.NFS3.name());

0 commit comments

Comments
 (0)