@xen-orchestra/backups 0.36.0 → 0.37.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.
Files changed (35) hide show
  1. package/Backup.js +11 -302
  2. package/ImportVmBackup.js +6 -6
  3. package/RemoteAdapter.js +20 -13
  4. package/RestoreMetadataBackup.js +1 -1
  5. package/_backupWorker.js +2 -2
  6. package/{_deltaVm.js → _incrementalVm.js} +11 -11
  7. package/_runners/Metadata.js +134 -0
  8. package/_runners/VmsXapi.js +138 -0
  9. package/_runners/_Abstract.js +51 -0
  10. package/{_PoolMetadataBackup.js → _runners/_PoolMetadataBackup.js} +3 -3
  11. package/_runners/_RemoteTimeoutError.js +8 -0
  12. package/{_XoMetadataBackup.js → _runners/_XoMetadataBackup.js} +3 -3
  13. package/_runners/_getAdaptersByRemote.js +9 -0
  14. package/_runners/_runTask.js +6 -0
  15. package/_runners/_vmRunners/FullXapi.js +61 -0
  16. package/_runners/_vmRunners/IncrementalXapi.js +163 -0
  17. package/_runners/_vmRunners/_Abstract.js +87 -0
  18. package/_runners/_vmRunners/_AbstractXapi.js +258 -0
  19. package/_runners/_vmRunners/_forkDeltaExport.js +12 -0
  20. package/{writers/FullBackupWriter.js → _runners/_writers/FullRemoteWriter.js} +5 -5
  21. package/{writers/FullReplicationWriter.js → _runners/_writers/FullXapiWriter.js} +5 -5
  22. package/{writers/DeltaBackupWriter.js → _runners/_writers/IncrementalRemoteWriter.js} +7 -7
  23. package/{writers/DeltaReplicationWriter.js → _runners/_writers/IncrementalXapiWriter.js} +10 -10
  24. package/{writers/_AbstractDeltaWriter.js → _runners/_writers/_AbstractIncrementalWriter.js} +1 -1
  25. package/{writers/_MixinBackupWriter.js → _runners/_writers/_MixinRemoteWriter.js} +10 -10
  26. package/{writers/_MixinReplicationWriter.js → _runners/_writers/_MixinXapiWriter.js} +6 -12
  27. package/package.json +6 -6
  28. package/_VmBackup.js +0 -515
  29. /package/{_createStreamThrottle.js → _runners/_createStreamThrottle.js} +0 -0
  30. /package/{_forkStreamUnpipe.js → _runners/_forkStreamUnpipe.js} +0 -0
  31. /package/{writers → _runners/_writers}/_AbstractFullWriter.js +0 -0
  32. /package/{writers → _runners/_writers}/_AbstractWriter.js +0 -0
  33. /package/{writers → _runners/_writers}/_checkVhd.js +0 -0
  34. /package/{writers → _runners/_writers}/_listReplicatedVms.js +0 -0
  35. /package/{writers → _runners/_writers}/_packUuid.js +0 -0
@@ -0,0 +1,258 @@
1
+ 'use strict'
2
+
3
+ const assert = require('assert')
4
+ const groupBy = require('lodash/groupBy.js')
5
+ const ignoreErrors = require('promise-toolbox/ignoreErrors')
6
+ const { asyncMap } = require('@xen-orchestra/async-map')
7
+ const { decorateMethodsWith } = require('@vates/decorate-with')
8
+ const { defer } = require('golike-defer')
9
+ const { formatDateTime } = require('@xen-orchestra/xapi')
10
+
11
+ const { getOldEntries } = require('../../_getOldEntries.js')
12
+ const { Task } = require('../../Task.js')
13
+ const { Abstract } = require('./_Abstract.js')
14
+
15
+ class AbstractXapiVmBackupRunner extends Abstract {
16
+ constructor({
17
+ config,
18
+ getSnapshotNameLabel,
19
+ healthCheckSr,
20
+ job,
21
+ remoteAdapters,
22
+ remotes,
23
+ schedule,
24
+ settings,
25
+ srs,
26
+ throttleStream,
27
+ vm,
28
+ }) {
29
+ super()
30
+ if (vm.other_config['xo:backup:job'] === job.id && 'start' in vm.blocked_operations) {
31
+ // don't match replicated VMs created by this very job otherwise they
32
+ // will be replicated again and again
33
+ throw new Error('cannot backup a VM created by this very job')
34
+ }
35
+
36
+ this.config = config
37
+ this.job = job
38
+ this.remoteAdapters = remoteAdapters
39
+ this.scheduleId = schedule.id
40
+ this.timestamp = undefined
41
+
42
+ // VM currently backed up
43
+ this.vm = vm
44
+ const { tags } = this.vm
45
+
46
+ // VM (snapshot) that is really exported
47
+ this.exportedVm = undefined
48
+
49
+ this._fullVdisRequired = undefined
50
+ this._getSnapshotNameLabel = getSnapshotNameLabel
51
+ this._isIncremental = job.mode === 'delta'
52
+ this._healthCheckSr = healthCheckSr
53
+ this._jobId = job.id
54
+ this._jobSnapshots = undefined
55
+ this._throttleStream = throttleStream
56
+ this._xapi = vm.$xapi
57
+
58
+ // Base VM for the export
59
+ this._baseVm = undefined
60
+
61
+ // Settings for this specific run (job, schedule, VM)
62
+ if (tags.includes('xo-memory-backup')) {
63
+ settings.checkpointSnapshot = true
64
+ }
65
+ if (tags.includes('xo-offline-backup')) {
66
+ settings.offlineSnapshot = true
67
+ }
68
+ this._settings = settings
69
+
70
+ // Create writers
71
+ {
72
+ const writers = new Set()
73
+ this._writers = writers
74
+
75
+ const [BackupWriter, ReplicationWriter] = this._getWriters()
76
+
77
+ const allSettings = job.settings
78
+ Object.keys(remoteAdapters).forEach(remoteId => {
79
+ const targetSettings = {
80
+ ...settings,
81
+ ...allSettings[remoteId],
82
+ }
83
+ if (targetSettings.exportRetention !== 0) {
84
+ writers.add(new BackupWriter({ backup: this, remoteId, settings: targetSettings }))
85
+ }
86
+ })
87
+ srs.forEach(sr => {
88
+ const targetSettings = {
89
+ ...settings,
90
+ ...allSettings[sr.uuid],
91
+ }
92
+ if (targetSettings.copyRetention !== 0) {
93
+ writers.add(new ReplicationWriter({ backup: this, sr, settings: targetSettings }))
94
+ }
95
+ })
96
+ }
97
+ }
98
+
99
+ // ensure the VM itself does not have any backup metadata which would be
100
+ // copied on manual snapshots and interfere with the backup jobs
101
+ async _cleanMetadata() {
102
+ const { vm } = this
103
+ if ('xo:backup:job' in vm.other_config) {
104
+ await vm.update_other_config({
105
+ 'xo:backup:datetime': null,
106
+ 'xo:backup:deltaChainLength': null,
107
+ 'xo:backup:exported': null,
108
+ 'xo:backup:job': null,
109
+ 'xo:backup:schedule': null,
110
+ 'xo:backup:vm': null,
111
+ })
112
+ }
113
+ }
114
+
115
+ async _snapshot() {
116
+ const { vm } = this
117
+ const xapi = this._xapi
118
+
119
+ const settings = this._settings
120
+
121
+ if (this._mustDoSnapshot()) {
122
+ await Task.run({ name: 'snapshot' }, async () => {
123
+ if (!settings.bypassVdiChainsCheck) {
124
+ await vm.$assertHealthyVdiChains()
125
+ }
126
+
127
+ const snapshotRef = await vm[settings.checkpointSnapshot ? '$checkpoint' : '$snapshot']({
128
+ ignoreNobakVdis: true,
129
+ name_label: this._getSnapshotNameLabel(vm),
130
+ unplugVusbs: true,
131
+ })
132
+ this.timestamp = Date.now()
133
+
134
+ await xapi.setFieldEntries('VM', snapshotRef, 'other_config', {
135
+ 'xo:backup:datetime': formatDateTime(this.timestamp),
136
+ 'xo:backup:job': this._jobId,
137
+ 'xo:backup:schedule': this.scheduleId,
138
+ 'xo:backup:vm': vm.uuid,
139
+ })
140
+
141
+ this.exportedVm = await xapi.getRecord('VM', snapshotRef)
142
+
143
+ return this.exportedVm.uuid
144
+ })
145
+ } else {
146
+ this.exportedVm = vm
147
+ this.timestamp = Date.now()
148
+ }
149
+ }
150
+
151
+ async _fetchJobSnapshots() {
152
+ const jobId = this._jobId
153
+ const vmRef = this.vm.$ref
154
+ const xapi = this._xapi
155
+
156
+ const snapshotsRef = await xapi.getField('VM', vmRef, 'snapshots')
157
+ const snapshotsOtherConfig = await asyncMap(snapshotsRef, ref => xapi.getField('VM', ref, 'other_config'))
158
+
159
+ const snapshots = []
160
+ snapshotsOtherConfig.forEach((other_config, i) => {
161
+ if (other_config['xo:backup:job'] === jobId) {
162
+ snapshots.push({ other_config, $ref: snapshotsRef[i] })
163
+ }
164
+ })
165
+ snapshots.sort((a, b) => (a.other_config['xo:backup:datetime'] < b.other_config['xo:backup:datetime'] ? -1 : 1))
166
+ this._jobSnapshots = snapshots
167
+ }
168
+
169
+ async _removeUnusedSnapshots() {
170
+ const allSettings = this.job.settings
171
+ const baseSettings = this._baseSettings
172
+ const baseVmRef = this._baseVm?.$ref
173
+
174
+ const snapshotsPerSchedule = groupBy(this._jobSnapshots, _ => _.other_config['xo:backup:schedule'])
175
+ const xapi = this._xapi
176
+ await asyncMap(Object.entries(snapshotsPerSchedule), ([scheduleId, snapshots]) => {
177
+ const settings = {
178
+ ...baseSettings,
179
+ ...allSettings[scheduleId],
180
+ ...allSettings[this.vm.uuid],
181
+ }
182
+ return asyncMap(getOldEntries(settings.snapshotRetention, snapshots), ({ $ref }) => {
183
+ if ($ref !== baseVmRef) {
184
+ return xapi.VM_destroy($ref)
185
+ }
186
+ })
187
+ })
188
+ }
189
+
190
+ async copy() {
191
+ throw new Error('Not implemented')
192
+ }
193
+
194
+ _getWriters() {
195
+ throw new Error('Not implemented')
196
+ }
197
+
198
+ _mustDoSnapshot() {
199
+ throw new Error('Not implemented')
200
+ }
201
+
202
+ async _selectBaseVm() {
203
+ throw new Error('Not implemented')
204
+ }
205
+
206
+ async run($defer) {
207
+ const settings = this._settings
208
+ assert(
209
+ !settings.offlineBackup || settings.snapshotRetention === 0,
210
+ 'offlineBackup is not compatible with snapshotRetention'
211
+ )
212
+
213
+ await this._callWriters(async writer => {
214
+ await writer.beforeBackup()
215
+ $defer(async () => {
216
+ await writer.afterBackup()
217
+ })
218
+ }, 'writer.beforeBackup()')
219
+
220
+ await this._fetchJobSnapshots()
221
+
222
+ await this._selectBaseVm()
223
+
224
+ await this._cleanMetadata()
225
+ await this._removeUnusedSnapshots()
226
+
227
+ const { vm } = this
228
+ const isRunning = vm.power_state === 'Running'
229
+ const startAfter = isRunning && (settings.offlineBackup ? 'backup' : settings.offlineSnapshot && 'snapshot')
230
+ if (startAfter) {
231
+ await vm.$callAsync('clean_shutdown')
232
+ }
233
+
234
+ try {
235
+ await this._snapshot()
236
+ if (startAfter === 'snapshot') {
237
+ ignoreErrors.call(vm.$callAsync('start', false, false))
238
+ }
239
+
240
+ if (this._writers.size !== 0) {
241
+ await this._copy()
242
+ }
243
+ } finally {
244
+ if (startAfter) {
245
+ ignoreErrors.call(vm.$callAsync('start', false, false))
246
+ }
247
+
248
+ await this._fetchJobSnapshots()
249
+ await this._removeUnusedSnapshots()
250
+ }
251
+ await this._healthCheck()
252
+ }
253
+ }
254
+ exports.AbstractXapi = AbstractXapiVmBackupRunner
255
+
256
+ decorateMethodsWith(AbstractXapiVmBackupRunner, {
257
+ run: defer,
258
+ })
@@ -0,0 +1,12 @@
1
+ 'use strict'
2
+
3
+ const { mapValues } = require('lodash')
4
+ const { forkStreamUnpipe } = require('../_forkStreamUnpipe')
5
+
6
+ exports.forkDeltaExport = function forkDeltaExport(deltaExport) {
7
+ return Object.create(deltaExport, {
8
+ streams: {
9
+ value: mapValues(deltaExport.streams, forkStreamUnpipe),
10
+ },
11
+ })
12
+ }
@@ -1,13 +1,13 @@
1
1
  'use strict'
2
2
 
3
- const { formatFilenameDate } = require('../_filenameDate.js')
4
- const { getOldEntries } = require('../_getOldEntries.js')
5
- const { Task } = require('../Task.js')
3
+ const { formatFilenameDate } = require('../../_filenameDate.js')
4
+ const { getOldEntries } = require('../../_getOldEntries.js')
5
+ const { Task } = require('../../Task.js')
6
6
 
7
- const { MixinBackupWriter } = require('./_MixinBackupWriter.js')
7
+ const { MixinRemoteWriter } = require('./_MixinRemoteWriter.js')
8
8
  const { AbstractFullWriter } = require('./_AbstractFullWriter.js')
9
9
 
10
- exports.FullBackupWriter = class FullBackupWriter extends MixinBackupWriter(AbstractFullWriter) {
10
+ exports.FullRemoteWriter = class FullRemoteWriter extends MixinRemoteWriter(AbstractFullWriter) {
11
11
  constructor(props) {
12
12
  super(props)
13
13
 
@@ -4,15 +4,15 @@ const ignoreErrors = require('promise-toolbox/ignoreErrors')
4
4
  const { asyncMap, asyncMapSettled } = require('@xen-orchestra/async-map')
5
5
  const { formatDateTime } = require('@xen-orchestra/xapi')
6
6
 
7
- const { formatFilenameDate } = require('../_filenameDate.js')
8
- const { getOldEntries } = require('../_getOldEntries.js')
9
- const { Task } = require('../Task.js')
7
+ const { formatFilenameDate } = require('../../_filenameDate.js')
8
+ const { getOldEntries } = require('../../_getOldEntries.js')
9
+ const { Task } = require('../../Task.js')
10
10
 
11
11
  const { AbstractFullWriter } = require('./_AbstractFullWriter.js')
12
- const { MixinReplicationWriter } = require('./_MixinReplicationWriter.js')
12
+ const { MixinXapiWriter } = require('./_MixinXapiWriter.js')
13
13
  const { listReplicatedVms } = require('./_listReplicatedVms.js')
14
14
 
15
- exports.FullReplicationWriter = class FullReplicationWriter extends MixinReplicationWriter(AbstractFullWriter) {
15
+ exports.FullXapiWriter = class FullXapiWriter extends MixinXapiWriter(AbstractFullWriter) {
16
16
  constructor(props) {
17
17
  super(props)
18
18
 
@@ -11,19 +11,19 @@ const { decorateClass } = require('@vates/decorate-with')
11
11
  const { defer } = require('golike-defer')
12
12
  const { dirname } = require('path')
13
13
 
14
- const { formatFilenameDate } = require('../_filenameDate.js')
15
- const { getOldEntries } = require('../_getOldEntries.js')
16
- const { Task } = require('../Task.js')
14
+ const { formatFilenameDate } = require('../../_filenameDate.js')
15
+ const { getOldEntries } = require('../../_getOldEntries.js')
16
+ const { Task } = require('../../Task.js')
17
17
 
18
- const { MixinBackupWriter } = require('./_MixinBackupWriter.js')
19
- const { AbstractDeltaWriter } = require('./_AbstractDeltaWriter.js')
18
+ const { MixinRemoteWriter } = require('./_MixinRemoteWriter.js')
19
+ const { AbstractIncrementalWriter } = require('./_AbstractIncrementalWriter.js')
20
20
  const { checkVhd } = require('./_checkVhd.js')
21
21
  const { packUuid } = require('./_packUuid.js')
22
22
  const { Disposable } = require('promise-toolbox')
23
23
 
24
24
  const { warn } = createLogger('xo:backups:DeltaBackupWriter')
25
25
 
26
- class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
26
+ class IncrementalRemoteWriter extends MixinRemoteWriter(AbstractIncrementalWriter) {
27
27
  async checkBaseVdis(baseUuidToSrcVdi) {
28
28
  const { handler } = this._adapter
29
29
  const backup = this._backup
@@ -227,6 +227,6 @@ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
227
227
  // TODO: run cleanup?
228
228
  }
229
229
  }
230
- exports.DeltaBackupWriter = decorateClass(DeltaBackupWriter, {
230
+ exports.IncrementalRemoteWriter = decorateClass(IncrementalRemoteWriter, {
231
231
  _transfer: defer,
232
232
  })
@@ -4,16 +4,16 @@ const { asyncMap, asyncMapSettled } = require('@xen-orchestra/async-map')
4
4
  const ignoreErrors = require('promise-toolbox/ignoreErrors')
5
5
  const { formatDateTime } = require('@xen-orchestra/xapi')
6
6
 
7
- const { formatFilenameDate } = require('../_filenameDate.js')
8
- const { getOldEntries } = require('../_getOldEntries.js')
9
- const { importDeltaVm, TAG_COPY_SRC } = require('../_deltaVm.js')
10
- const { Task } = require('../Task.js')
7
+ const { formatFilenameDate } = require('../../_filenameDate.js')
8
+ const { getOldEntries } = require('../../_getOldEntries.js')
9
+ const { importIncrementalVm, TAG_COPY_SRC } = require('../../_incrementalVm.js')
10
+ const { Task } = require('../../Task.js')
11
11
 
12
- const { AbstractDeltaWriter } = require('./_AbstractDeltaWriter.js')
13
- const { MixinReplicationWriter } = require('./_MixinReplicationWriter.js')
12
+ const { AbstractIncrementalWriter } = require('./_AbstractIncrementalWriter.js')
13
+ const { MixinXapiWriter } = require('./_MixinXapiWriter.js')
14
14
  const { listReplicatedVms } = require('./_listReplicatedVms.js')
15
15
 
16
- exports.DeltaReplicationWriter = class DeltaReplicationWriter extends MixinReplicationWriter(AbstractDeltaWriter) {
16
+ exports.IncrementalXapiWriter = class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWriter) {
17
17
  async checkBaseVdis(baseUuidToSrcVdi, baseVm) {
18
18
  const sr = this._sr
19
19
  const replicatedVm = listReplicatedVms(sr.$xapi, this._backup.job.id, sr.uuid, this._backup.vm.uuid).find(
@@ -50,8 +50,8 @@ exports.DeltaReplicationWriter = class DeltaReplicationWriter extends MixinRepli
50
50
  },
51
51
  })
52
52
  this.transfer = task.wrapFn(this.transfer)
53
- this.healthCheck = task.wrapFn(this.healthCheck)
54
- this.cleanup = task.wrapFn(this.cleanup, true)
53
+ this.cleanup = task.wrapFn(this.cleanup)
54
+ this.healthCheck = task.wrapFn(this.healthCheck, true)
55
55
 
56
56
  return task.run(() => this._prepare())
57
57
  }
@@ -90,7 +90,7 @@ exports.DeltaReplicationWriter = class DeltaReplicationWriter extends MixinRepli
90
90
 
91
91
  let targetVmRef
92
92
  await Task.run({ name: 'transfer' }, async () => {
93
- targetVmRef = await importDeltaVm(
93
+ targetVmRef = await importIncrementalVm(
94
94
  {
95
95
  __proto__: deltaExport,
96
96
  vm: {
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { AbstractWriter } = require('./_AbstractWriter.js')
4
4
 
5
- exports.AbstractDeltaWriter = class AbstractDeltaWriter extends AbstractWriter {
5
+ exports.AbstractIncrementalWriter = class AbstractIncrementalWriter extends AbstractWriter {
6
6
  checkBaseVdis(baseUuidToSrcVdi, baseVm) {
7
7
  throw new Error('Not implemented')
8
8
  }
@@ -4,17 +4,17 @@ const { createLogger } = require('@xen-orchestra/log')
4
4
  const { join } = require('path')
5
5
 
6
6
  const assert = require('assert')
7
- const { formatFilenameDate } = require('../_filenameDate.js')
8
- const { getVmBackupDir } = require('../_getVmBackupDir.js')
9
- const { HealthCheckVmBackup } = require('../HealthCheckVmBackup.js')
10
- const { ImportVmBackup } = require('../ImportVmBackup.js')
11
- const { Task } = require('../Task.js')
12
- const MergeWorker = require('../merge-worker/index.js')
7
+ const { formatFilenameDate } = require('../../_filenameDate.js')
8
+ const { getVmBackupDir } = require('../../_getVmBackupDir.js')
9
+ const { HealthCheckVmBackup } = require('../../HealthCheckVmBackup.js')
10
+ const { ImportVmBackup } = require('../../ImportVmBackup.js')
11
+ const { Task } = require('../../Task.js')
12
+ const MergeWorker = require('../../merge-worker/index.js')
13
13
 
14
14
  const { info, warn } = createLogger('xo:backups:MixinBackupWriter')
15
15
 
16
- exports.MixinBackupWriter = (BaseClass = Object) =>
17
- class MixinBackupWriter extends BaseClass {
16
+ exports.MixinRemoteWriter = (BaseClass = Object) =>
17
+ class MixinRemoteWriter extends BaseClass {
18
18
  #lock
19
19
 
20
20
  constructor({ remoteId, ...rest }) {
@@ -58,7 +58,7 @@ exports.MixinBackupWriter = (BaseClass = Object) =>
58
58
  const { disableMergeWorker } = this._backup.config
59
59
  // merge worker only compatible with local remotes
60
60
  const { handler } = this._adapter
61
- const willMergeInWorker = !disableMergeWorker && typeof handler._getRealPath === 'function'
61
+ const willMergeInWorker = !disableMergeWorker && typeof handler.getRealPath === 'function'
62
62
 
63
63
  const { merge } = await this._cleanVm({ remove: true, merge: !willMergeInWorker })
64
64
  await this.#lock.dispose()
@@ -71,7 +71,7 @@ exports.MixinBackupWriter = (BaseClass = Object) =>
71
71
  Math.random().toString(36).slice(2)
72
72
 
73
73
  await handler.outputFile(taskFile, this._backup.vm.uuid)
74
- const remotePath = handler._getRealPath()
74
+ const remotePath = handler.getRealPath()
75
75
  await MergeWorker.run(remotePath)
76
76
  }
77
77
  }
@@ -1,19 +1,13 @@
1
1
  'use strict'
2
2
 
3
- const { Task } = require('../Task')
3
+ const { extractOpaqueRef } = require('@xen-orchestra/xapi')
4
+
5
+ const { Task } = require('../../Task')
4
6
  const assert = require('node:assert/strict')
5
- const { HealthCheckVmBackup } = require('../HealthCheckVmBackup')
7
+ const { HealthCheckVmBackup } = require('../../HealthCheckVmBackup')
6
8
 
7
- function extractOpaqueRef(str) {
8
- const OPAQUE_REF_RE = /OpaqueRef:[0-9a-z-]+/
9
- const matches = OPAQUE_REF_RE.exec(str)
10
- if (!matches) {
11
- throw new Error('no opaque ref found')
12
- }
13
- return matches[0]
14
- }
15
- exports.MixinReplicationWriter = (BaseClass = Object) =>
16
- class MixinReplicationWriter extends BaseClass {
9
+ exports.MixinXapiWriter = (BaseClass = Object) =>
10
+ class MixinXapiWriter extends BaseClass {
17
11
  constructor({ sr, ...rest }) {
18
12
  super(rest)
19
13
 
package/package.json CHANGED
@@ -8,13 +8,13 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/vatesfr/xen-orchestra.git"
10
10
  },
11
- "version": "0.36.0",
11
+ "version": "0.37.0",
12
12
  "engines": {
13
13
  "node": ">=14.6"
14
14
  },
15
15
  "scripts": {
16
16
  "postversion": "npm publish --access public",
17
- "test": "node--test"
17
+ "test-integration": "node--test *.integ.js"
18
18
  },
19
19
  "dependencies": {
20
20
  "@kldzj/stream-throttle": "^1.1.1",
@@ -27,7 +27,7 @@
27
27
  "@vates/nbd-client": "^1.2.0",
28
28
  "@vates/parse-duration": "^0.1.1",
29
29
  "@xen-orchestra/async-map": "^0.1.2",
30
- "@xen-orchestra/fs": "^3.3.4",
30
+ "@xen-orchestra/fs": "^4.0.0",
31
31
  "@xen-orchestra/log": "^0.6.0",
32
32
  "@xen-orchestra/template": "^0.1.0",
33
33
  "compare-versions": "^5.0.1",
@@ -42,17 +42,17 @@
42
42
  "promise-toolbox": "^0.21.0",
43
43
  "proper-lockfile": "^4.1.2",
44
44
  "uuid": "^9.0.0",
45
- "vhd-lib": "^4.4.0",
45
+ "vhd-lib": "^4.4.1",
46
46
  "yazl": "^2.5.1"
47
47
  },
48
48
  "devDependencies": {
49
- "rimraf": "^4.1.1",
49
+ "rimraf": "^5.0.1",
50
50
  "sinon": "^15.0.1",
51
51
  "test": "^3.2.1",
52
52
  "tmp": "^0.2.1"
53
53
  },
54
54
  "peerDependencies": {
55
- "@xen-orchestra/xapi": "^2.2.0"
55
+ "@xen-orchestra/xapi": "^2.2.1"
56
56
  },
57
57
  "license": "AGPL-3.0-or-later",
58
58
  "author": {