@xen-orchestra/backups 0.50.0 → 0.52.0

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.
@@ -15,6 +15,7 @@ const noop = Function.prototype
15
15
 
16
16
  const DEFAULT_XAPI_VM_SETTINGS = {
17
17
  bypassVdiChainsCheck: false,
18
+ cbtDestroySnapshotData: false,
18
19
  checkpointSnapshot: false,
19
20
  concurrency: 2,
20
21
  copyRetention: 0,
@@ -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 ( fullInterval !== 0 && fullInterval < deltaChainLength + 1) {
151
- debug('not using base VM because fullInterval reached', { fullInterval, deltaChainLength, eq: fullInterval < deltaChainLength + 1, dc1: deltaChainLength + 1 })
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
 
@@ -157,7 +157,7 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
157
157
  }
158
158
 
159
159
  const snapshotRef = await vm[settings.checkpointSnapshot ? '$checkpoint' : '$snapshot']({
160
- ignoreNobakVdis: true,
160
+ ignoredVdisTag: '[NOBAK]',
161
161
  name_label: this._getSnapshotNameLabel(vm),
162
162
  unplugVusbs: true,
163
163
  })
@@ -227,12 +227,11 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
227
227
  ...allSettings[scheduleId],
228
228
  ...allSettings[this._vm.uuid],
229
229
  }
230
- // ensure we never delete the last one
231
- const retention = Math.max(settings.snapshotRetention ?? 0, 1)
232
-
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
233
  await asyncMap(getOldEntries(retention, datetimes), async datetime => {
234
234
  const vdis = snapshotPerDatetime[datetime]
235
-
236
235
  let vmRef
237
236
  // if there is an attached VM => destroy the VM (Non CBT backups)
238
237
  for (const vdi of vdis) {
@@ -274,10 +273,11 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
274
273
  this._exportedVm?.is_a_snapshot &&
275
274
  // user don't want to keep the snapshot data
276
275
  this._settings.snapshotRetention === 0 &&
277
- // preferNbd is not a guarantee that the backup used NBD, depending on the network configuration
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
278
  this._settings.preferNbd &&
279
279
  // only delete snapshost data if the config allows it
280
- this.config.purgeSnapshotData
280
+ this._settings.cbtDestroySnapshotData
281
281
  ) {
282
282
  Task.info('will delete snapshot data')
283
283
  const vdiRefs = await this._xapi.VM_getDisks(this._exportedVm?.$ref)
@@ -285,7 +285,7 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
285
285
  for (const vdiRef of vdiRefs) {
286
286
  try {
287
287
  // data_destroy will fail with a VDI_NO_CBT_METADATA error if CBT is not enabled on this VDI
288
- await xapi.VDI_dataDestroy(vdiRef)
288
+ await xapi.call('VDI.data_destroy', vdiRef)
289
289
  Task.info(`Snapshot data has been deleted`, { vdiRef })
290
290
  } catch (error) {
291
291
  Task.warning(`Couldn't deleted snapshot data`, { error, vdiRef })
@@ -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(({ other_config }) => {
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?.[COPY_OF])
25
+ return baseUuidToSrcVdi.has(vdi?.other_config[COPY_OF])
26
26
  })
27
27
  .map(({ other_config }) => other_config?.[COPY_OF])
28
28
  .filter(_ => !!_)
@@ -52,10 +52,10 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
52
52
  this.cleanup = task.wrapFn(this.cleanup, !hasHealthCheckSr)
53
53
  this.healthCheck = task.wrapFn(this.healthCheck, hasHealthCheckSr)
54
54
 
55
- return task.run(() => this._prepare())
55
+ return task.run(() => this._prepare(isFull))
56
56
  }
57
57
 
58
- async _prepare() {
58
+ async _prepare(isFull) {
59
59
  const settings = this._settings
60
60
  const { uuid: srUuid, $xapi: xapi } = this._sr
61
61
  const vmUuid = this._vmUuid
@@ -67,14 +67,19 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
67
67
  this._oldEntries = getOldEntries(settings.copyRetention - 1, listReplicatedVms(xapi, scheduleId, srUuid, vmUuid))
68
68
 
69
69
  if (settings.deleteFirst) {
70
+ // we want to keep the baseVM when copying a delta
71
+ // even if we want to keep only one after
72
+ let mostRecentEntry
73
+ if (this._oldEntries.length > 1 && settings.copyRetention === 1 && !isFull) {
74
+ mostRecentEntry = this._oldEntries.pop()
75
+ }
70
76
  await this._deleteOldEntries()
77
+ this._oldEntries = mostRecentEntry !== undefined ? [mostRecentEntry] : []
71
78
  }
72
79
  }
73
80
 
74
81
  async cleanup() {
75
- if (!this._settings.deleteFirst) {
76
- await this._deleteOldEntries()
77
- }
82
+ await this._deleteOldEntries()
78
83
  }
79
84
 
80
85
  async _deleteOldEntries() {
@@ -98,10 +103,11 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
98
103
  .filter(_ => !!_)
99
104
  // @todo use index ?
100
105
 
101
- const replicatedVdis = sr.$VDIs.filter(({ other_config }) => {
102
- // REPLICATED_TO_SR_UUID is not used here since we are already filtering from sr.$VDIs
103
- return sourceVdiUuids.includes(other_config?.[COPY_OF])
104
- })
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
+ })
105
111
 
106
112
  Object.values(backup.vdis).forEach(vdi => {
107
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.50.0",
11
+ "version": "0.52.0",
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.10.0",
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": "^6.1.0"
61
+ "@xen-orchestra/xapi": "^7.0.0"
62
62
  },
63
63
  "license": "AGPL-3.0-or-later",
64
64
  "author": {