@xen-orchestra/backups 0.29.4 → 0.29.6

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/Backup.js CHANGED
@@ -38,7 +38,7 @@ const DEFAULT_VM_SETTINGS = {
38
38
  fullInterval: 0,
39
39
  healthCheckSr: undefined,
40
40
  healthCheckVmsWithTags: [],
41
- maxMergedDeltasPerRun: 2,
41
+ maxMergedDeltasPerRun: Infinity,
42
42
  offlineBackup: false,
43
43
  offlineSnapshot: false,
44
44
  snapshotRetention: 0,
package/README.md CHANGED
@@ -8,8 +8,8 @@
8
8
 
9
9
  Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/backups):
10
10
 
11
- ```
12
- > npm install --save @xen-orchestra/backups
11
+ ```sh
12
+ npm install --save @xen-orchestra/backups
13
13
  ```
14
14
 
15
15
  ## Contributions
package/RemoteAdapter.js CHANGED
@@ -28,6 +28,7 @@ const { isMetadataFile } = require('./_backupType.js')
28
28
  const { isValidXva } = require('./_isValidXva.js')
29
29
  const { listPartitions, LVM_PARTITION_TYPE } = require('./_listPartitions.js')
30
30
  const { lvs, pvs } = require('./_lvm.js')
31
+ const { watchStreamSize } = require('./_watchStreamSize')
31
32
  // @todo : this import is marked extraneous , sould be fixed when lib is published
32
33
  const { mount } = require('@vates/fuse-vhd')
33
34
  const { asyncEach } = require('@vates/async-each')
@@ -661,7 +662,7 @@ class RemoteAdapter {
661
662
  const handler = this._handler
662
663
  if (this.#useVhdDirectory()) {
663
664
  const dataPath = `${dirname(path)}/data/${uuidv4()}.vhd`
664
- await createVhdDirectoryFromStream(handler, dataPath, input, {
665
+ const size = await createVhdDirectoryFromStream(handler, dataPath, input, {
665
666
  concurrency: writeBlockConcurrency,
666
667
  compression: this.#getCompressionType(),
667
668
  async validator() {
@@ -671,12 +672,14 @@ class RemoteAdapter {
671
672
  nbdClient,
672
673
  })
673
674
  await VhdAbstract.createAlias(handler, path, dataPath)
675
+ return size
674
676
  } else {
675
- await this.outputStream(path, input, { checksum, validator })
677
+ return this.outputStream(path, input, { checksum, validator })
676
678
  }
677
679
  }
678
680
 
679
681
  async outputStream(path, input, { checksum = true, validator = noop } = {}) {
682
+ const container = watchStreamSize(input)
680
683
  await this._handler.outputStream(path, input, {
681
684
  checksum,
682
685
  dirMode: this._dirMode,
@@ -685,6 +688,7 @@ class RemoteAdapter {
685
688
  return validator.apply(this, arguments)
686
689
  },
687
690
  })
691
+ return container.size
688
692
  }
689
693
 
690
694
  // open the hierarchy of ancestors until we find a full one
package/Task.js CHANGED
@@ -100,7 +100,7 @@ class Task {
100
100
  * In case of error, the task will be failed.
101
101
  *
102
102
  * @typedef Result
103
- * @param {() => Result)} fn
103
+ * @param {() => Result} fn
104
104
  * @param {boolean} last - Whether the task should succeed if there is no error
105
105
  * @returns Result
106
106
  */
package/_backupWorker.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- require('@xen-orchestra/log/configure.js').catchGlobalErrors(
3
+ require('@xen-orchestra/log/configure').catchGlobalErrors(
4
4
  require('@xen-orchestra/log').createLogger('xo:backups:worker')
5
5
  )
6
6
 
package/_deltaVm.js CHANGED
@@ -258,6 +258,9 @@ exports.importDeltaVm = defer(async function importDeltaVm(
258
258
  $defer.onFailure(() => newVdi.$destroy())
259
259
 
260
260
  await newVdi.update_other_config(TAG_COPY_SRC, vdi.uuid)
261
+ if (vdi.virtual_size > newVdi.virtual_size) {
262
+ await newVdi.$callAsync('resize', vdi.virtual_size)
263
+ }
261
264
  } else if (vdiRef === vmRecord.suspend_VDI) {
262
265
  // suspendVDI has already created
263
266
  newVdi = suspendVdi
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const eos = require('end-of-stream')
4
- const { PassThrough } = require('stream')
3
+ const { finished, PassThrough } = require('node:stream')
5
4
 
6
5
  const { debug } = require('@xen-orchestra/log').createLogger('xo:backups:forkStreamUnpipe')
7
6
 
@@ -9,29 +8,29 @@ const { debug } = require('@xen-orchestra/log').createLogger('xo:backups:forkStr
9
8
  //
10
9
  // in case of error in the new readable stream, it will simply be unpiped
11
10
  // from the original one
12
- exports.forkStreamUnpipe = function forkStreamUnpipe(stream) {
13
- const { forks = 0 } = stream
14
- stream.forks = forks + 1
11
+ exports.forkStreamUnpipe = function forkStreamUnpipe(source) {
12
+ const { forks = 0 } = source
13
+ source.forks = forks + 1
15
14
 
16
- debug('forking', { forks: stream.forks })
15
+ debug('forking', { forks: source.forks })
17
16
 
18
- const proxy = new PassThrough()
19
- stream.pipe(proxy)
20
- eos(stream, error => {
17
+ const fork = new PassThrough()
18
+ source.pipe(fork)
19
+ finished(source, { writable: false }, error => {
21
20
  if (error !== undefined) {
22
21
  debug('error on original stream, destroying fork', { error })
23
- proxy.destroy(error)
22
+ fork.destroy(error)
24
23
  }
25
24
  })
26
- eos(proxy, error => {
27
- debug('end of stream, unpiping', { error, forks: --stream.forks })
25
+ finished(fork, { readable: false }, error => {
26
+ debug('end of stream, unpiping', { error, forks: --source.forks })
28
27
 
29
- stream.unpipe(proxy)
28
+ source.unpipe(fork)
30
29
 
31
- if (stream.forks === 0) {
30
+ if (source.forks === 0) {
32
31
  debug('no more forks, destroying original stream')
33
- stream.destroy(new Error('no more consumers for this stream'))
32
+ source.destroy(new Error('no more consumers for this stream'))
34
33
  }
35
34
  })
36
- return proxy
35
+ return fork
37
36
  }
@@ -4,7 +4,7 @@
4
4
 
5
5
  'use strict'
6
6
 
7
- const { catchGlobalErrors } = require('@xen-orchestra/log/configure.js')
7
+ const { catchGlobalErrors } = require('@xen-orchestra/log/configure')
8
8
  const { createLogger } = require('@xen-orchestra/log')
9
9
  const { getSyncedHandler } = require('@xen-orchestra/fs')
10
10
  const { join } = require('path')
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.29.4",
11
+ "version": "0.29.6",
12
12
  "engines": {
13
13
  "node": ">=14.6"
14
14
  },
@@ -21,38 +21,37 @@
21
21
  "@vates/cached-dns.lookup": "^1.0.0",
22
22
  "@vates/compose": "^2.1.0",
23
23
  "@vates/decorate-with": "^2.0.0",
24
- "@vates/disposable": "^0.1.3",
24
+ "@vates/disposable": "^0.1.4",
25
25
  "@vates/fuse-vhd": "^1.0.0",
26
- "@vates/nbd-client": "*",
26
+ "@vates/nbd-client": "^1.0.1",
27
27
  "@vates/parse-duration": "^0.1.1",
28
28
  "@xen-orchestra/async-map": "^0.1.2",
29
- "@xen-orchestra/fs": "^3.3.0",
30
- "@xen-orchestra/log": "^0.5.0",
29
+ "@xen-orchestra/fs": "^3.3.1",
30
+ "@xen-orchestra/log": "^0.6.0",
31
31
  "@xen-orchestra/template": "^0.1.0",
32
32
  "compare-versions": "^5.0.1",
33
33
  "d3-time-format": "^3.0.0",
34
34
  "decorator-synchronized": "^0.6.0",
35
- "end-of-stream": "^1.4.4",
36
- "fs-extra": "^10.0.0",
35
+ "fs-extra": "^11.1.0",
37
36
  "golike-defer": "^0.5.1",
38
37
  "limit-concurrency-decorator": "^0.5.0",
39
38
  "lodash": "^4.17.20",
40
39
  "node-zone": "^0.4.0",
41
- "parse-pairs": "^1.1.0",
40
+ "parse-pairs": "^2.0.0",
42
41
  "promise-toolbox": "^0.21.0",
43
42
  "proper-lockfile": "^4.1.2",
44
43
  "uuid": "^9.0.0",
45
- "vhd-lib": "^4.2.0",
44
+ "vhd-lib": "^4.2.1",
46
45
  "yazl": "^2.5.1"
47
46
  },
48
47
  "devDependencies": {
49
- "rimraf": "^3.0.2",
50
- "sinon": "^14.0.1",
48
+ "rimraf": "^4.1.1",
49
+ "sinon": "^15.0.1",
51
50
  "test": "^3.2.1",
52
51
  "tmp": "^0.2.1"
53
52
  },
54
53
  "peerDependencies": {
55
- "@xen-orchestra/xapi": "^1.6.0"
54
+ "@xen-orchestra/xapi": "^1.6.1"
56
55
  },
57
56
  "license": "AGPL-3.0-or-later",
58
57
  "author": {
@@ -7,6 +7,8 @@ const ignoreErrors = require('promise-toolbox/ignoreErrors')
7
7
  const { asyncMap } = require('@xen-orchestra/async-map')
8
8
  const { chainVhd, checkVhdChain, openVhd, VhdAbstract } = require('vhd-lib')
9
9
  const { createLogger } = require('@xen-orchestra/log')
10
+ const { decorateClass } = require('@vates/decorate-with')
11
+ const { defer } = require('golike-defer')
10
12
  const { dirname } = require('path')
11
13
 
12
14
  const { formatFilenameDate } = require('../_filenameDate.js')
@@ -20,9 +22,9 @@ const { packUuid } = require('./_packUuid.js')
20
22
  const { Disposable } = require('promise-toolbox')
21
23
  const NbdClient = require('@vates/nbd-client')
22
24
 
23
- const { debug, warn } = createLogger('xo:backups:DeltaBackupWriter')
25
+ const { debug, warn, info } = createLogger('xo:backups:DeltaBackupWriter')
24
26
 
25
- exports.DeltaBackupWriter = class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
27
+ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
26
28
  async checkBaseVdis(baseUuidToSrcVdi) {
27
29
  const { handler } = this._adapter
28
30
  const backup = this._backup
@@ -133,7 +135,7 @@ exports.DeltaBackupWriter = class DeltaBackupWriter extends MixinBackupWriter(Ab
133
135
  }
134
136
  }
135
137
 
136
- async _transfer({ timestamp, deltaExport, sizeContainers }) {
138
+ async _transfer($defer, { timestamp, deltaExport }) {
137
139
  const adapter = this._adapter
138
140
  const backup = this._backup
139
141
 
@@ -172,6 +174,7 @@ exports.DeltaBackupWriter = class DeltaBackupWriter extends MixinBackupWriter(Ab
172
174
  }
173
175
 
174
176
  const { size } = await Task.run({ name: 'transfer' }, async () => {
177
+ let transferSize = 0
175
178
  await Promise.all(
176
179
  map(deltaExport.vdis, async (vdi, id) => {
177
180
  const path = `${this._vmBackupDir}/${vhds[id]}`
@@ -200,21 +203,30 @@ exports.DeltaBackupWriter = class DeltaBackupWriter extends MixinBackupWriter(Ab
200
203
  const vdiRef = vm.$xapi.getObject(vdi.uuid).$ref
201
204
 
202
205
  let nbdClient
203
- if (!this._backup.config.useNbd) {
206
+ if (this._backup.config.useNbd) {
207
+ debug('useNbd is enabled', { vdi: id, path })
204
208
  // get nbd if possible
205
209
  try {
206
210
  // this will always take the first host in the list
207
211
  const [nbdInfo] = await vm.$xapi.call('VDI.get_nbd_info', vdiRef)
212
+ debug('got NBD info', { nbdInfo, vdi: id, path })
208
213
  nbdClient = new NbdClient(nbdInfo)
209
214
  await nbdClient.connect()
210
- debug(`got nbd connection `, { vdi: vdi.uuid })
215
+
216
+ // this will inform the xapi that we don't need this anymore
217
+ // and will detach the vdi from dom0
218
+ $defer(() => nbdClient.disconnect())
219
+
220
+ info('NBD client ready', { vdi: id, path })
211
221
  } catch (error) {
212
222
  nbdClient = undefined
213
- debug(`can't connect to nbd server or no server available`, { error })
223
+ warn('error connecting to NBD server', { error, vdi: id, path })
214
224
  }
225
+ } else {
226
+ debug('useNbd is disabled', { vdi: id, path })
215
227
  }
216
228
 
217
- await adapter.writeVhd(path, deltaExport.streams[`${id}.vhd`], {
229
+ transferSize += await adapter.writeVhd(path, deltaExport.streams[`${id}.vhd`], {
218
230
  // no checksum for VHDs, because they will be invalidated by
219
231
  // merges and chainings
220
232
  checksum: false,
@@ -235,9 +247,7 @@ exports.DeltaBackupWriter = class DeltaBackupWriter extends MixinBackupWriter(Ab
235
247
  })
236
248
  })
237
249
  )
238
- return {
239
- size: Object.values(sizeContainers).reduce((sum, { size }) => sum + size, 0),
240
- }
250
+ return { size: transferSize }
241
251
  })
242
252
  metadataContent.size = size
243
253
  this._metadataFileName = await adapter.writeVmBackupMetadata(vm.uuid, metadataContent)
@@ -245,3 +255,6 @@ exports.DeltaBackupWriter = class DeltaBackupWriter extends MixinBackupWriter(Ab
245
255
  // TODO: run cleanup?
246
256
  }
247
257
  }
258
+ exports.DeltaBackupWriter = decorateClass(DeltaBackupWriter, {
259
+ _transfer: defer,
260
+ })