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#12792Damans227 wants to merge 2 commits intoapache:4.22from
Conversation
|
@blueorangutan package |
|
@Damans227 a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 4.22 #12792 +/- ##
=========================================
Coverage 17.61% 17.61%
- Complexity 15664 15667 +3
=========================================
Files 5917 5917
Lines 531402 531412 +10
Branches 64971 64972 +1
=========================================
+ Hits 93596 93606 +10
Misses 427252 427252
Partials 10554 10554
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Packaging result [SF]: ✖️ el8 ✖️ el9 ✔️ debian ✖️ suse15. SL-JID 17097 |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 17104 |
There was a problem hiding this comment.
Pull request overview
This PR prevents VM backup deletion when there are in-progress async jobs that depend on the same backup (notably create-VM-from-backup and restore), avoiding failures caused by deleting a backup mid-operation.
Changes:
- Added a pending-async-job check to
BackupManagerImpl.deleteBackup()to block deletion when related jobs are in progress. - Added a unit test covering the “delete blocked by pending jobs” scenario.
- Updated existing test stubbing to include
backup.getUuid()where required by the new logic.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java |
Adds checkForPendingBackupJobs and calls it from deleteBackup to prevent deletion while dependent jobs are in progress. |
server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java |
Introduces a new unit test ensuring deletion is blocked when countPendingJobs(...) reports pending work. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| long pendingJobs = asyncJobManager.countPendingJobs(backupUuid, | ||
| CreateVMFromBackupCmd.class.getName(), | ||
| CreateVMFromBackupCmdByAdmin.class.getName(), | ||
| RestoreBackupCmd.class.getName()); |
There was a problem hiding this comment.
checkForPendingBackupJobs only checks for CreateVMFromBackup* and RestoreBackupCmd. There is also an async restore operation RestoreVolumeFromBackupAndAttachToVMCmd that uses a backup as input; deleting the backup while that job is in progress can fail the restore similarly. Consider including that command class in the pending-jobs check (or centralizing a list of “backup-consuming” job types).
| RestoreBackupCmd.class.getName()); | |
| RestoreBackupCmd.class.getName(), | |
| RestoreVolumeFromBackupAndAttachToVMCmd.class.getName()); |
| CreateVMFromBackupCmdByAdmin.class.getName(), | ||
| RestoreBackupCmd.class.getName()); | ||
| if (pendingJobs > 0) { | ||
| throw new CloudRuntimeException("Cannot delete backup while a create instance from backup or restore operation is in progress, please try again later."); |
There was a problem hiding this comment.
The exception message has awkward grammar (“while a create instance…”) and doesn’t identify the backup being blocked. Consider rephrasing to reference the actual API operations (createVMFromBackup/restoreBackup) and include the backup ID/UUID so operators can correlate the failure quickly.
| throw new CloudRuntimeException("Cannot delete backup while a create instance from backup or restore operation is in progress, please try again later."); | |
| throw new CloudRuntimeException(String.format( | |
| "Cannot delete backup with UUID [%s] while a createVMFromBackup or restoreBackup operation is in progress (pending jobs: %d). Please try again later.", | |
| backupUuid, pendingJobs)); |
| @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(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")).thenReturn(1L); | ||
|
|
||
| backupManager.deleteBackup(backupId, false); | ||
| } |
There was a problem hiding this comment.
This test uses @Test(expected=...), which can pass even if deleteBackup throws for a different reason. To make the intent explicit, consider using assertThrows and asserting the exception message and/or verifying that backupProvider.deleteBackup(...) is never invoked when countPendingJobs(...) > 0.
| checkForPendingBackupJobs(backup); | ||
|
|
||
| validateBackupForZone(backup.getZoneId()); | ||
| accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm == null ? backup : vm); |
There was a problem hiding this comment.
checkForPendingBackupJobs(backup) is executed before accountManager.checkAccess(...). This changes the error precedence (a caller without access could now get the “pending job” error) and it also runs an extra DB query before authorization. Consider moving the pending-job check to after the access check (and after validateBackupForZone(...) if you want config/zone validation to take precedence).
| checkForPendingBackupJobs(backup); | |
| validateBackupForZone(backup.getZoneId()); | |
| accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm == null ? backup : vm); | |
| validateBackupForZone(backup.getZoneId()); | |
| accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm == null ? backup : vm); | |
| checkForPendingBackupJobs(backup); |
Description
Fixes: #11356
Adds a check in
deleteBackup()to block deletion when acreateVMFromBackuporrestoreBackupasync job is in progress for that backup.Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
Screen.Recording.2026-03-12.at.10.00.41.AM.mp4
How Has This Been Tested?
createVMFromBackup, then attempteddeleteBackupon same backup, confirmed it now throwsCloudRuntimeExceptiontestDeleteBackupBlockedByPendingJobsHow did you try to break this feature and the system with this change?
deleteBackupstill succeeds when no pending jobs exist