fix(backups): handle diskless vm snapshot retention (#9758)

* fix(backups): handle diskless vm snapshot retention
This commit is contained in:
Florent BEAUCHAMP
2026-04-29 10:25:23 +02:00
committed by GitHub
parent b83a7f9758
commit 12fb701686
2 changed files with 40 additions and 2 deletions

View File

@@ -338,6 +338,20 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
}
this._jobSnapshotVdis = Object.values(vdiCandidates)
// For VMs with no disks, retention must be tracked directly on VM snapshots
// since there are no VDIs to anchor the discovery.
if (vdiUuids.length === 0) {
this._disklessJobSnapshotVms = this._vm.$snapshots.filter(Boolean).filter(
({ other_config, $snapshot_of }) =>
$snapshot_of !== undefined &&
other_config[JOB_ID] === jobId &&
other_config[VM_UUID] === this._vm.uuid &&
other_config[COPY_OF] === undefined
)
} else {
this._disklessJobSnapshotVms = []
}
}
async _removeUnusedSnapshots() {
@@ -349,9 +363,10 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
await xapi.barrier()
// ensure cached object are up to date
this._jobSnapshotVdis = this._jobSnapshotVdis.map(vdi => xapi.getObject(vdi.$ref))
const disklessVmSnapshots = this._disklessJobSnapshotVms.map(vm => xapi.getObject(vm.$ref))
// get the datetime of the most recent snapshot
const lastSnapshotDateTime = this._jobSnapshotVdis
// get the datetime of the most recent snapshot across both VDI and diskless VM snapshots
const lastSnapshotDateTime = [...this._jobSnapshotVdis, ...disklessVmSnapshots]
.map(({ other_config }) => other_config[DATETIME])
.sort()
.pop()
@@ -428,6 +443,28 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
})
})
// Retention for VMs with no disks: VM snapshots are not reachable via VDIs
if (disklessVmSnapshots.length > 0) {
const snapshotsPerSchedule = groupBy(disklessVmSnapshots, _ => _.other_config[SCHEDULE_ID])
await asyncEach(Object.entries(snapshotsPerSchedule), async ([scheduleId, snapshots]) => {
// we only have one snapshot per date time since it's at the VM level
const snapshotPerDatetime = Object.fromEntries(snapshots.map(s => [s.other_config[DATETIME], s.$ref]))
const datetimes = Object.keys(snapshotPerDatetime).sort()
const settings = {
...baseSettings,
...allSettings[scheduleId],
...allSettings[this._vm.uuid],
}
const retention = settings.snapshotRetention ?? 0
await asyncEach(getOldEntries(retention, datetimes), async datetime => {
if (this.job.mode === 'delta' && datetime === lastSnapshotDateTime) {
return
}
await xapi.VM_destroy(snapshotPerDatetime[datetime])
})
})
}
// list and remove the snapshot were the jobs failed between
// makesnapshot and update_other_config
const snapshots = this._vm.$snapshots.filter(_ => !!_).filter(({ name_label }) => name_label === TEMP_SNAPSHOT_NAME)

View File

@@ -32,6 +32,7 @@
- [Backup] Fix cleanVm incorrect backup size in metadata (PR [#9637] (https://github.com/vatesfr/xen-orchestra/pull/9637))
- [xo-server] Fix VM-template still visible after deletion (PR [#9760](https://github.com/vatesfr/xen-orchestra/pull/9760))
- [Backups] Fix diskless snapshot retention (PR [#9758](https://github.com/vatesfr/xen-orchestra/pull/9758))
- **XO 5**:
- [Icons] Fix display of RHEL 10 icons in vm list (PR [#9766](https://github.com/vatesfr/xen-orchestra/pull/9766))
- [xo-server-sdn-controller] Better traffic-rules synchronization related to VM lifecycle (PR [#9518](https://github.com/vatesfr/xen-orchestra/pull/9518))