@xen-orchestra/backups 0.51.0 → 0.52.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/_backupWorker.mjs
CHANGED
|
@@ -23,7 +23,7 @@ createCachedLookup().patchGlobal()
|
|
|
23
23
|
|
|
24
24
|
const logger = createLogger('xo:backups:worker')
|
|
25
25
|
catchGlobalErrors(logger)
|
|
26
|
-
const { debug, info } = logger
|
|
26
|
+
const { debug, info, warn } = logger
|
|
27
27
|
|
|
28
28
|
class BackupWorker {
|
|
29
29
|
#config
|
|
@@ -204,6 +204,13 @@ process.on('message', async message => {
|
|
|
204
204
|
info('backup has ended')
|
|
205
205
|
await ignoreErrors.call(backupWorker.debounceResource.flushAll())
|
|
206
206
|
process.disconnect()
|
|
207
|
+
|
|
208
|
+
setTimeout(() => {
|
|
209
|
+
warn('worker process did not exit automatically, forcing...')
|
|
210
|
+
|
|
211
|
+
// eslint-disable-next-line n/no-process-exit
|
|
212
|
+
process.exit()
|
|
213
|
+
}, 30e3).unref()
|
|
207
214
|
}
|
|
208
215
|
}
|
|
209
216
|
})
|
package/_runners/VmsXapi.mjs
CHANGED
|
@@ -147,8 +147,13 @@ export const IncrementalXapi = class IncrementalXapiVmBackupRunner extends Abstr
|
|
|
147
147
|
...lastExportedVdis.map(({ other_config }) => Number(other_config[DELTA_CHAIN_LENGTH] ?? 0))
|
|
148
148
|
)
|
|
149
149
|
const fullInterval = this._settings.fullInterval
|
|
150
|
-
if (
|
|
151
|
-
debug('not using base VM because fullInterval reached', {
|
|
150
|
+
if (fullInterval !== 0 && fullInterval <= deltaChainLength + 1) {
|
|
151
|
+
debug('not using base VM because fullInterval reached', {
|
|
152
|
+
fullInterval,
|
|
153
|
+
deltaChainLength,
|
|
154
|
+
eq: fullInterval < deltaChainLength + 1,
|
|
155
|
+
dc1: deltaChainLength + 1,
|
|
156
|
+
})
|
|
152
157
|
return
|
|
153
158
|
}
|
|
154
159
|
|
|
@@ -146,6 +146,15 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
|
146
146
|
if (!settings.bypassVdiChainsCheck) {
|
|
147
147
|
await vm.$assertHealthyVdiChains()
|
|
148
148
|
}
|
|
149
|
+
if (settings.preferNbd) {
|
|
150
|
+
try {
|
|
151
|
+
// enable CBT on all disks if possible
|
|
152
|
+
const diskRefs = await xapi.VM_getDisks(vm.$ref)
|
|
153
|
+
await Promise.all(diskRefs.map(diskRef => xapi.call('VDI.enable_cbt', diskRef)))
|
|
154
|
+
} catch (error) {
|
|
155
|
+
Task.info(`couldn't enable CBT`, error)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
149
158
|
|
|
150
159
|
const snapshotRef = await vm[settings.checkpointSnapshot ? '$checkpoint' : '$snapshot']({
|
|
151
160
|
ignoredVdisTag: '[NOBAK]',
|
|
@@ -160,7 +169,6 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
|
160
169
|
vmUuid: vm.uuid,
|
|
161
170
|
})
|
|
162
171
|
this._exportedVm = await xapi.getRecord('VM', snapshotRef)
|
|
163
|
-
|
|
164
172
|
return this._exportedVm.uuid
|
|
165
173
|
})
|
|
166
174
|
} else {
|
|
@@ -208,7 +216,7 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
|
208
216
|
|
|
209
217
|
const snapshotsPerSchedule = groupBy(this._jobSnapshotVdis, _ => _.other_config[SCHEDULE_ID])
|
|
210
218
|
const xapi = this._xapi
|
|
211
|
-
await asyncMap(Object.entries(snapshotsPerSchedule), ([scheduleId, snapshots]) => {
|
|
219
|
+
await asyncMap(Object.entries(snapshotsPerSchedule), async ([scheduleId, snapshots]) => {
|
|
212
220
|
const snapshotPerDatetime = groupBy(snapshots, _ => _.other_config[DATETIME])
|
|
213
221
|
|
|
214
222
|
const datetimes = Object.keys(snapshotPerDatetime)
|
|
@@ -219,12 +227,11 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
|
219
227
|
...allSettings[scheduleId],
|
|
220
228
|
...allSettings[this._vm.uuid],
|
|
221
229
|
}
|
|
222
|
-
// ensure we never delete the last one
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
230
|
+
// ensure we never delete the last one for delta
|
|
231
|
+
const minRetention = this.job.mode === 'delta' ? 1 : 0
|
|
232
|
+
const retention = Math.max(settings.snapshotRetention ?? 0, minRetention)
|
|
233
|
+
await asyncMap(getOldEntries(retention, datetimes), async datetime => {
|
|
226
234
|
const vdis = snapshotPerDatetime[datetime]
|
|
227
|
-
|
|
228
235
|
let vmRef
|
|
229
236
|
// if there is an attached VM => destroy the VM (Non CBT backups)
|
|
230
237
|
for (const vdi of vdis) {
|
|
@@ -234,8 +241,8 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
|
234
241
|
// this will throw error for VDI still attached to control domain
|
|
235
242
|
assert.strictEqual(vbds.length, 1, 'VDI must be free or attached to exactly one VM')
|
|
236
243
|
const vm = vbds[0].$VM
|
|
244
|
+
assert.strictEqual(vm.is_control_domain, false, `Disk is still attached to DOM0 VM`) // don't delete a VM (especially a control domain)
|
|
237
245
|
assert.strictEqual(vm.is_a_snapshot, true, `VM must be a snapshot`) // don't delete a VM (especially a control domain)
|
|
238
|
-
assert.strictEqual(vm.is_control_domain, false, `VM can't be a DOM0 VM`) // don't delete a VM (especially a control domain)
|
|
239
246
|
|
|
240
247
|
const vmRefVdi = vm.$ref
|
|
241
248
|
// same vm than other vdi of the same batch
|
|
@@ -257,6 +264,34 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
|
257
264
|
}
|
|
258
265
|
})
|
|
259
266
|
})
|
|
267
|
+
|
|
268
|
+
// now that we use CBT, we can destroy the data of the snapshot used for this backup
|
|
269
|
+
// going back to a previous version of XO not supporting CBT will create a full backup
|
|
270
|
+
// this will only do something after snapshot and transfer
|
|
271
|
+
if (
|
|
272
|
+
// don't modify the VM
|
|
273
|
+
this._exportedVm?.is_a_snapshot &&
|
|
274
|
+
// user don't want to keep the snapshot data
|
|
275
|
+
this._settings.snapshotRetention === 0 &&
|
|
276
|
+
// preferNbd is not a guarantee that the backup used NBD, depending on the network configuration,
|
|
277
|
+
// in that case next runs will be full, but there is not an easy way to prevent that
|
|
278
|
+
this._settings.preferNbd &&
|
|
279
|
+
// only delete snapshost data if the config allows it
|
|
280
|
+
this._settings.cbtDestroySnapshotData
|
|
281
|
+
) {
|
|
282
|
+
Task.info('will delete snapshot data')
|
|
283
|
+
const vdiRefs = await this._xapi.VM_getDisks(this._exportedVm?.$ref)
|
|
284
|
+
await xapi.call('VM.destroy', this._exportedVm.$ref)
|
|
285
|
+
for (const vdiRef of vdiRefs) {
|
|
286
|
+
try {
|
|
287
|
+
// data_destroy will fail with a VDI_NO_CBT_METADATA error if CBT is not enabled on this VDI
|
|
288
|
+
await xapi.call('VDI.data_destroy', vdiRef)
|
|
289
|
+
Task.info(`Snapshot data has been deleted`, { vdiRef })
|
|
290
|
+
} catch (error) {
|
|
291
|
+
Task.warning(`Couldn't deleted snapshot data`, { error, vdiRef })
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
260
295
|
}
|
|
261
296
|
|
|
262
297
|
async copy() {
|
|
@@ -19,10 +19,10 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
|
|
|
19
19
|
// @todo use an index if possible
|
|
20
20
|
// @todo : this seems similare to decorateVmMetadata
|
|
21
21
|
|
|
22
|
-
const replicatedVdis = sr.$VDIs
|
|
23
|
-
.filter(
|
|
22
|
+
const replicatedVdis = sr.$VDIs
|
|
23
|
+
.filter(vdi => {
|
|
24
24
|
// REPLICATED_TO_SR_UUID is not used here since we are already filtering from sr.$VDIs
|
|
25
|
-
return baseUuidToSrcVdi.has(other_config
|
|
25
|
+
return baseUuidToSrcVdi.has(vdi?.other_config[COPY_OF])
|
|
26
26
|
})
|
|
27
27
|
.map(({ other_config }) => other_config?.[COPY_OF])
|
|
28
28
|
.filter(_ => !!_)
|
|
@@ -103,10 +103,11 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
|
|
|
103
103
|
.filter(_ => !!_)
|
|
104
104
|
// @todo use index ?
|
|
105
105
|
|
|
106
|
-
const replicatedVdis = sr.$VDIs
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
const replicatedVdis = sr.$VDIs
|
|
107
|
+
.filter(vdi => {
|
|
108
|
+
// REPLICATED_TO_SR_UUID is not used here since we are already filtering from sr.$VDIs
|
|
109
|
+
return sourceVdiUuids.includes(vdi?.other_config[COPY_OF])
|
|
110
|
+
})
|
|
110
111
|
|
|
111
112
|
Object.values(backup.vdis).forEach(vdi => {
|
|
112
113
|
vdi.other_config[COPY_OF] = vdi.uuid
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
|
10
10
|
},
|
|
11
|
-
"version": "0.
|
|
11
|
+
"version": "0.52.1",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=14.18"
|
|
14
14
|
},
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"proper-lockfile": "^4.1.2",
|
|
47
47
|
"tar": "^6.1.15",
|
|
48
48
|
"uuid": "^9.0.0",
|
|
49
|
-
"vhd-lib": "^4.
|
|
49
|
+
"vhd-lib": "^4.11.0",
|
|
50
50
|
"xen-api": "^4.0.0",
|
|
51
51
|
"yazl": "^2.5.1"
|
|
52
52
|
},
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"tmp": "^0.2.1"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@xen-orchestra/xapi": "^7.
|
|
61
|
+
"@xen-orchestra/xapi": "^7.1.0"
|
|
62
62
|
},
|
|
63
63
|
"license": "AGPL-3.0-or-later",
|
|
64
64
|
"author": {
|