@xen-orchestra/backups 0.42.1 → 0.43.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/RemoteAdapter.mjs CHANGED
@@ -681,11 +681,13 @@ export class RemoteAdapter {
681
681
  }
682
682
  }
683
683
 
684
- async outputStream(path, input, { checksum = true, validator = noop } = {}) {
684
+ async outputStream(path, input, { checksum = true, maxStreamLength, streamLength, validator = noop } = {}) {
685
685
  const container = watchStreamSize(input)
686
686
  await this._handler.outputStream(path, input, {
687
687
  checksum,
688
688
  dirMode: this._dirMode,
689
+ maxStreamLength,
690
+ streamLength,
689
691
  async validator() {
690
692
  await input.task
691
693
  return validator.apply(this, arguments)
@@ -29,6 +29,8 @@ export const FullRemote = class FullRemoteVmBackupRunner extends AbstractRemote
29
29
  writer =>
30
30
  writer.run({
31
31
  stream: forkStreamUnpipe(stream),
32
+ // stream will be forked and transformed, it's not safe to attach additionnal properties to it
33
+ streamLength: stream.length,
32
34
  timestamp: metadata.timestamp,
33
35
  vm: metadata.vm,
34
36
  vmSnapshot: metadata.vmSnapshot,
@@ -35,13 +35,23 @@ export const FullXapi = class FullXapiVmBackupRunner extends AbstractXapi {
35
35
  useSnapshot: false,
36
36
  })
37
37
  )
38
+
39
+ const vdis = await exportedVm.$getDisks()
40
+ let maxStreamLength = 1024 * 1024 // Ovf file and tar headers are a few KB, let's stay safe
41
+ for (const vdiRef of vdis) {
42
+ const vdi = await this._xapi.getRecord(vdiRef)
43
+ // at most the xva will take the physical usage of the disk
44
+ // the resulting stream can be smaller due to the smaller block size for xva than vhd, and compression of xcp-ng
45
+ maxStreamLength += vdi.physical_utilisation
46
+ }
47
+
38
48
  const sizeContainer = watchStreamSize(stream)
39
49
 
40
50
  const timestamp = Date.now()
41
-
42
51
  await this._callWriters(
43
52
  writer =>
44
53
  writer.run({
54
+ maxStreamLength,
45
55
  sizeContainer,
46
56
  stream: forkStreamUnpipe(stream),
47
57
  timestamp,
@@ -31,6 +31,11 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
31
31
  throw new Error('cannot backup a VM created by this very job')
32
32
  }
33
33
 
34
+ const currentOperations = Object.values(vm.current_operations)
35
+ if (currentOperations.some(_ => _ === 'migrate_send' || _ === 'pool_migrate')) {
36
+ throw new Error('cannot backup a VM currently being migrated')
37
+ }
38
+
34
39
  this.config = config
35
40
  this.job = job
36
41
  this.remoteAdapters = remoteAdapters
@@ -256,7 +261,15 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
256
261
  }
257
262
 
258
263
  if (this._writers.size !== 0) {
259
- await this._copy()
264
+ const { pool_migrate = null, migrate_send = null } = this._exportedVm.blocked_operations
265
+
266
+ const reason = 'VM migration is blocked during backup'
267
+ await this._exportedVm.update_blocked_operations({ pool_migrate: reason, migrate_send: reason })
268
+ try {
269
+ await this._copy()
270
+ } finally {
271
+ await this._exportedVm.update_blocked_operations({ pool_migrate, migrate_send })
272
+ }
260
273
  }
261
274
  } finally {
262
275
  if (startAfter) {
@@ -24,7 +24,7 @@ export class FullRemoteWriter extends MixinRemoteWriter(AbstractFullWriter) {
24
24
  )
25
25
  }
26
26
 
27
- async _run({ timestamp, sizeContainer, stream, vm, vmSnapshot }) {
27
+ async _run({ maxStreamLength, timestamp, sizeContainer, stream, streamLength, vm, vmSnapshot }) {
28
28
  const settings = this._settings
29
29
  const job = this._job
30
30
  const scheduleId = this._scheduleId
@@ -65,6 +65,8 @@ export class FullRemoteWriter extends MixinRemoteWriter(AbstractFullWriter) {
65
65
 
66
66
  await Task.run({ name: 'transfer' }, async () => {
67
67
  await adapter.outputStream(dataFilename, stream, {
68
+ maxStreamLength,
69
+ streamLength,
68
70
  validator: tmpPath => adapter.isValidXva(tmpPath),
69
71
  })
70
72
  return { size: sizeContainer.size }
@@ -1,9 +1,9 @@
1
1
  import { AbstractWriter } from './_AbstractWriter.mjs'
2
2
 
3
3
  export class AbstractFullWriter extends AbstractWriter {
4
- async run({ timestamp, sizeContainer, stream, vm, vmSnapshot }) {
4
+ async run({ maxStreamLength, timestamp, sizeContainer, stream, streamLength, vm, vmSnapshot }) {
5
5
  try {
6
- return await this._run({ timestamp, sizeContainer, stream, vm, vmSnapshot })
6
+ return await this._run({ maxStreamLength, timestamp, sizeContainer, stream, streamLength, vm, vmSnapshot })
7
7
  } finally {
8
8
  // ensure stream is properly closed
9
9
  stream.destroy()
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.42.1",
11
+ "version": "0.43.1",
12
12
  "engines": {
13
13
  "node": ">=14.18"
14
14
  },
@@ -28,7 +28,7 @@
28
28
  "@vates/nbd-client": "^2.0.0",
29
29
  "@vates/parse-duration": "^0.1.1",
30
30
  "@xen-orchestra/async-map": "^0.1.2",
31
- "@xen-orchestra/fs": "^4.1.0",
31
+ "@xen-orchestra/fs": "^4.1.1",
32
32
  "@xen-orchestra/log": "^0.6.0",
33
33
  "@xen-orchestra/template": "^0.1.0",
34
34
  "app-conf": "^2.3.0",
@@ -44,7 +44,7 @@
44
44
  "proper-lockfile": "^4.1.2",
45
45
  "tar": "^6.1.15",
46
46
  "uuid": "^9.0.0",
47
- "vhd-lib": "^4.6.0",
47
+ "vhd-lib": "^4.6.1",
48
48
  "xen-api": "^1.3.6",
49
49
  "yazl": "^2.5.1"
50
50
  },
@@ -56,7 +56,7 @@
56
56
  "tmp": "^0.2.1"
57
57
  },
58
58
  "peerDependencies": {
59
- "@xen-orchestra/xapi": "^3.1.0"
59
+ "@xen-orchestra/xapi": "^3.3.0"
60
60
  },
61
61
  "license": "AGPL-3.0-or-later",
62
62
  "author": {