Block backup deletion while create-VM-from-backup or restore jobs are in progress (#12792)

* Block backup deletion while create-VM-from-backup or restore jobs are in progress

* Add tests

* Fix exception message

* Update test

Co-authored-by: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com>
This commit is contained in:
Daman Arora 2026-04-08 22:39:01 -07:00 committed by GitHub
parent 03de62bf38
commit 7ba5240b31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 0 deletions

View File

@ -1531,6 +1531,8 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
validateBackupForZone(backup.getZoneId());
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm == null ? backup : vm);
checkForPendingBackupJobs(backup);
final BackupOffering offering = backupOfferingDao.findByIdIncludingRemoved(backup.getBackupOfferingId());
if (offering == null) {
throw new CloudRuntimeException(String.format("Backup offering with ID [%s] does not exist.", backup.getBackupOfferingId()));
@ -1551,6 +1553,18 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
throw new CloudRuntimeException("Failed to delete the backup");
}
private void checkForPendingBackupJobs(final BackupVO backup) {
String backupUuid = backup.getUuid();
long pendingJobs = asyncJobManager.countPendingJobs(backupUuid,
CreateVMFromBackupCmd.class.getName(),
CreateVMFromBackupCmdByAdmin.class.getName(),
RestoreBackupCmd.class.getName(),
RestoreVolumeFromBackupAndAttachToVMCmd.class.getName());
if (pendingJobs > 0) {
throw new CloudRuntimeException("Cannot delete Backup while a create Instance from Backup or restore Backup operation is in progress, please try again later.");
}
}
/**
* Get the pair: hostIp, datastoreUuid in which to restore the volume, based on the VM to be attached information
*/

View File

@ -91,6 +91,7 @@ import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
import org.junit.After;
import org.junit.Assert;
@ -241,6 +242,9 @@ public class BackupManagerTest {
@Mock
private GuestOSDao _guestOSDao;
@Mock
AsyncJobManager asyncJobManager;
private Gson gson;
private String[] hostPossibleValues = {"127.0.0.1", "hostname"};
@ -1489,6 +1493,7 @@ public class BackupManagerTest {
when(backup.getAccountId()).thenReturn(accountId);
when(backup.getBackupOfferingId()).thenReturn(backupOfferingId);
when(backup.getSize()).thenReturn(100L);
when(backup.getUuid()).thenReturn("backup-uuid");
overrideBackupFrameworkConfigValue();
@ -1523,6 +1528,31 @@ public class BackupManagerTest {
}
}
@Test(expected = CloudRuntimeException.class)
public void testDeleteBackupBlockedByPendingJobs() {
Long backupId = 1L;
Long vmId = 2L;
BackupVO backup = mock(BackupVO.class);
when(backup.getVmId()).thenReturn(vmId);
when(backup.getUuid()).thenReturn("backup-uuid");
when(backup.getZoneId()).thenReturn(1L);
when(backupDao.findByIdIncludingRemoved(backupId)).thenReturn(backup);
VMInstanceVO vm = mock(VMInstanceVO.class);
when(vmInstanceDao.findByIdIncludingRemoved(vmId)).thenReturn(vm);
overrideBackupFrameworkConfigValue();
when(asyncJobManager.countPendingJobs("backup-uuid",
"org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd",
"org.apache.cloudstack.api.command.admin.vm.CreateVMFromBackupCmdByAdmin",
"org.apache.cloudstack.api.command.user.backup.RestoreBackupCmd",
"org.apache.cloudstack.api.command.user.backup.RestoreVolumeFromBackupAndAttachToVMCmd")).thenReturn(1L);
backupManager.deleteBackup(backupId, false);
}
@Test
public void testNewBackupResponse() {
Long vmId = 1L;