@xen-orchestra/backups 0.38.2 → 0.39.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.
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { join, resolve } = require('node:path/posix')
4
+
3
5
  const { DIR_XO_POOL_METADATA_BACKUPS } = require('./RemoteAdapter.js')
4
6
  const { PATH_DB_DUMP } = require('./_runners/_PoolMetadataBackup.js')
5
7
 
@@ -20,7 +22,8 @@ exports.RestoreMetadataBackup = class RestoreMetadataBackup {
20
22
  task: xapi.task_create('Import pool metadata'),
21
23
  })
22
24
  } else {
23
- return String(await handler.readFile(`${backupId}/data.json`))
25
+ const metadata = JSON.parse(await handler.readFile(join(backupId, 'metadata.json')))
26
+ return String(await handler.readFile(resolve(backupId, metadata.data ?? 'data.json')))
24
27
  }
25
28
  }
26
29
  }
@@ -19,6 +19,7 @@ const DEFAULT_XAPI_VM_SETTINGS = {
19
19
  concurrency: 2,
20
20
  copyRetention: 0,
21
21
  deleteFirst: false,
22
+ diskPerVmConcurrency: 0, // not limited by default
22
23
  exportRetention: 0,
23
24
  fullInterval: 0,
24
25
  healthCheckSr: undefined,
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { asyncMap } = require('@xen-orchestra/async-map')
4
+ const { join } = require('@xen-orchestra/fs/path')
4
5
 
5
6
  const { DIR_XO_CONFIG_BACKUPS } = require('../RemoteAdapter.js')
6
7
  const { formatFilenameDate } = require('../_filenameDate.js')
@@ -23,10 +24,11 @@ exports.XoMetadataBackup = class XoMetadataBackup {
23
24
  const dir = `${scheduleDir}/${formatFilenameDate(timestamp)}`
24
25
 
25
26
  const data = job.xoMetadata
26
- const fileName = `${dir}/data.json`
27
+ const dataBaseName = './data.json'
27
28
 
28
29
  const metadata = JSON.stringify(
29
30
  {
31
+ data: dataBaseName,
30
32
  jobId: job.id,
31
33
  jobName: job.name,
32
34
  scheduleId: schedule.id,
@@ -36,6 +38,8 @@ exports.XoMetadataBackup = class XoMetadataBackup {
36
38
  null,
37
39
  2
38
40
  )
41
+
42
+ const dataFileName = join(dir, dataBaseName)
39
43
  const metaDataFileName = `${dir}/metadata.json`
40
44
 
41
45
  await asyncMap(
@@ -52,7 +56,7 @@ exports.XoMetadataBackup = class XoMetadataBackup {
52
56
  async () => {
53
57
  const handler = adapter.handler
54
58
  const dirMode = this._config.dirMode
55
- await handler.outputFile(fileName, data, { dirMode })
59
+ await handler.outputFile(dataFileName, data, { dirMode })
56
60
  await handler.outputFile(metaDataFileName, metadata, {
57
61
  dirMode,
58
62
  })
@@ -36,7 +36,7 @@ exports.FullXapiWriter = class FullXapiWriter extends MixinXapiWriter(AbstractFu
36
36
  const sr = this._sr
37
37
  const settings = this._settings
38
38
  const job = this._job
39
- const scheduleId = this.scheduleId
39
+ const scheduleId = this._scheduleId
40
40
 
41
41
  const { uuid: srUuid, $xapi: xapi } = sr
42
42
 
@@ -1,9 +1,9 @@
1
1
  'use strict'
2
2
 
3
3
  const assert = require('assert')
4
- const map = require('lodash/map.js')
5
4
  const mapValues = require('lodash/mapValues.js')
6
5
  const ignoreErrors = require('promise-toolbox/ignoreErrors')
6
+ const { asyncEach } = require('@vates/async-each')
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')
@@ -138,7 +138,7 @@ class IncrementalRemoteWriter extends MixinRemoteWriter(AbstractIncrementalWrite
138
138
  const adapter = this._adapter
139
139
  const job = this._job
140
140
  const scheduleId = this._scheduleId
141
-
141
+ const settings = this._settings
142
142
  const jobId = job.id
143
143
  const handler = adapter.handler
144
144
 
@@ -176,8 +176,9 @@ class IncrementalRemoteWriter extends MixinRemoteWriter(AbstractIncrementalWrite
176
176
  }
177
177
  const { size } = await Task.run({ name: 'transfer' }, async () => {
178
178
  let transferSize = 0
179
- await Promise.all(
180
- map(deltaExport.vdis, async (vdi, id) => {
179
+ await asyncEach(
180
+ Object.entries(deltaExport.vdis),
181
+ async ([id, vdi]) => {
181
182
  const path = `${this._vmBackupDir}/${vhds[id]}`
182
183
 
183
184
  const isDelta = differentialVhds[`${id}.vhd`]
@@ -223,8 +224,12 @@ class IncrementalRemoteWriter extends MixinRemoteWriter(AbstractIncrementalWrite
223
224
  await vhd.readBlockAllocationTable() // required by writeFooter()
224
225
  await vhd.writeFooter()
225
226
  })
226
- })
227
+ },
228
+ {
229
+ concurrency: settings.diskPerVmConcurrency,
230
+ }
227
231
  )
232
+
228
233
  return { size: transferSize }
229
234
  })
230
235
  metadataContent.size = size
@@ -14,6 +14,19 @@ exports.MixinXapiWriter = (BaseClass = Object) =>
14
14
  this._sr = sr
15
15
  }
16
16
 
17
+ // check if the base Vm has all its disk on health check sr
18
+ async #isAlreadyOnHealthCheckSr(baseVm) {
19
+ const xapi = baseVm.$xapi
20
+ const vdiRefs = await xapi.VM_getDisks(baseVm.$ref)
21
+ for (const vdiRef of vdiRefs) {
22
+ const vdi = xapi.getObject(vdiRef)
23
+ if (vdi.$SR.uuid !== this._heathCheckSr.uuid) {
24
+ return false
25
+ }
26
+ }
27
+ return true
28
+ }
29
+
17
30
  healthCheck() {
18
31
  const sr = this._healthCheckSr
19
32
  assert.notStrictEqual(sr, undefined, 'SR should be defined before making a health check')
@@ -25,20 +38,35 @@ exports.MixinXapiWriter = (BaseClass = Object) =>
25
38
  },
26
39
  async () => {
27
40
  const { $xapi: xapi } = sr
28
- let clonedVm
41
+ let healthCheckVmRef
29
42
  try {
30
43
  const baseVm = xapi.getObject(this._targetVmRef) ?? (await xapi.waitObject(this._targetVmRef))
31
- const clonedRef = await xapi
32
- .callAsync('VM.clone', this._targetVmRef, `Health Check - ${baseVm.name_label}`)
33
- .then(extractOpaqueRef)
34
- clonedVm = xapi.getObject(clonedRef) ?? (await xapi.waitObject(clonedRef))
44
+
45
+ if (await this.#isAlreadyOnHealthCheckSr(baseVm)) {
46
+ healthCheckVmRef = await Task.run(
47
+ { name: 'cloning-vm' },
48
+ async () =>
49
+ await xapi
50
+ .callAsync('VM.clone', this._targetVmRef, `Health Check - ${baseVm.name_label}`)
51
+ .then(extractOpaqueRef)
52
+ )
53
+ } else {
54
+ healthCheckVmRef = await Task.run(
55
+ { name: 'copying-vm' },
56
+ async () =>
57
+ await xapi
58
+ .callAsync('VM.copy', this._targetVmRef, `Health Check - ${baseVm.name_label}`, sr.$ref)
59
+ .then(extractOpaqueRef)
60
+ )
61
+ }
62
+ const healthCheckVm = xapi.getObject(healthCheckVmRef) ?? (await xapi.waitObject(healthCheckVmRef))
35
63
 
36
64
  await new HealthCheckVmBackup({
37
- restoredVm: clonedVm,
65
+ restoredVm: healthCheckVm,
38
66
  xapi,
39
67
  }).run()
40
68
  } finally {
41
- clonedVm && (await xapi.VM_destroy(clonedVm.$ref))
69
+ healthCheckVmRef && (await xapi.VM_destroy(healthCheckVmRef))
42
70
  }
43
71
  }
44
72
  )
package/package.json CHANGED
@@ -8,9 +8,9 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/vatesfr/xen-orchestra.git"
10
10
  },
11
- "version": "0.38.2",
11
+ "version": "0.39.0",
12
12
  "engines": {
13
- "node": ">=14.6"
13
+ "node": ">=14.18"
14
14
  },
15
15
  "scripts": {
16
16
  "postversion": "npm publish --access public",
@@ -24,10 +24,10 @@
24
24
  "@vates/decorate-with": "^2.0.0",
25
25
  "@vates/disposable": "^0.1.4",
26
26
  "@vates/fuse-vhd": "^1.0.0",
27
- "@vates/nbd-client": "^1.2.0",
27
+ "@vates/nbd-client": "^1.2.1",
28
28
  "@vates/parse-duration": "^0.1.1",
29
29
  "@xen-orchestra/async-map": "^0.1.2",
30
- "@xen-orchestra/fs": "^4.0.0",
30
+ "@xen-orchestra/fs": "^4.0.1",
31
31
  "@xen-orchestra/log": "^0.6.0",
32
32
  "@xen-orchestra/template": "^0.1.0",
33
33
  "compare-versions": "^5.0.1",
@@ -43,6 +43,7 @@
43
43
  "proper-lockfile": "^4.1.2",
44
44
  "uuid": "^9.0.0",
45
45
  "vhd-lib": "^4.5.0",
46
+ "xen-api": "^1.3.3",
46
47
  "yazl": "^2.5.1"
47
48
  },
48
49
  "devDependencies": {