@xen-orchestra/backups 0.29.6 → 0.30.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.
package/_deltaVm.js CHANGED
@@ -12,7 +12,7 @@ const { defer } = require('golike-defer')
12
12
 
13
13
  const { cancelableMap } = require('./_cancelableMap.js')
14
14
  const { Task } = require('./Task.js')
15
- const { pick } = require('lodash')
15
+ const pick = require('lodash/pick.js')
16
16
 
17
17
  const TAG_BASE_DELTA = 'xo:base_delta'
18
18
  exports.TAG_BASE_DELTA = TAG_BASE_DELTA
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.6",
11
+ "version": "0.30.0",
12
12
  "engines": {
13
13
  "node": ">=14.6"
14
14
  },
@@ -26,7 +26,7 @@
26
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.1",
29
+ "@xen-orchestra/fs": "^3.3.2",
30
30
  "@xen-orchestra/log": "^0.6.0",
31
31
  "@xen-orchestra/template": "^0.1.0",
32
32
  "compare-versions": "^5.0.1",
@@ -12,7 +12,7 @@ exports.runBackupWorker = function runBackupWorker(params, onLog) {
12
12
  return new Promise((resolve, reject) => {
13
13
  const worker = fork(PATH)
14
14
 
15
- worker.on('exit', code => reject(new Error(`worker exited with code ${code}`)))
15
+ worker.on('exit', (code, signal) => reject(new Error(`worker exited with code ${code} and signal ${signal}`)))
16
16
  worker.on('error', reject)
17
17
 
18
18
  worker.on('message', message => {
@@ -218,7 +218,9 @@ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
218
218
  $defer(() => nbdClient.disconnect())
219
219
 
220
220
  info('NBD client ready', { vdi: id, path })
221
+ Task.info('NBD used')
221
222
  } catch (error) {
223
+ Task.warning('NBD configured but unusable', { error })
222
224
  nbdClient = undefined
223
225
  warn('error connecting to NBD server', { error, vdi: id, path })
224
226
  }
@@ -80,6 +80,7 @@ exports.DeltaReplicationWriter = class DeltaReplicationWriter extends MixinRepli
80
80
  }
81
81
 
82
82
  async _transfer({ timestamp, deltaExport, sizeContainers }) {
83
+ const { _warmMigration } = this._settings
83
84
  const sr = this._sr
84
85
  const { job, scheduleId, vm } = this._backup
85
86
 
@@ -92,7 +93,7 @@ exports.DeltaReplicationWriter = class DeltaReplicationWriter extends MixinRepli
92
93
  __proto__: deltaExport,
93
94
  vm: {
94
95
  ...deltaExport.vm,
95
- tags: [...deltaExport.vm.tags, 'Continuous Replication'],
96
+ tags: _warmMigration ? deltaExport.vm.tags : [...deltaExport.vm.tags, 'Continuous Replication'],
96
97
  },
97
98
  },
98
99
  sr
@@ -101,11 +102,13 @@ exports.DeltaReplicationWriter = class DeltaReplicationWriter extends MixinRepli
101
102
  size: Object.values(sizeContainers).reduce((sum, { size }) => sum + size, 0),
102
103
  }
103
104
  })
104
-
105
+ this._targetVmRef = targetVmRef
105
106
  const targetVm = await xapi.getRecord('VM', targetVmRef)
106
107
 
107
108
  await Promise.all([
108
- targetVm.ha_restart_priority !== '' &&
109
+ // warm migration does not disable HA , since the goal is to start the new VM in production
110
+ !_warmMigration &&
111
+ targetVm.ha_restart_priority !== '' &&
109
112
  Promise.all([targetVm.set_ha_restart_priority(''), targetVm.add_tags('HA disabled')]),
110
113
  targetVm.set_name_label(`${vm.name_label} - ${job.name} - (${formatFilenameDate(timestamp)})`),
111
114
  asyncMap(['start', 'start_on'], op =>
@@ -46,7 +46,7 @@ exports.FullReplicationWriter = class FullReplicationWriter extends MixinReplica
46
46
  const oldVms = getOldEntries(settings.copyRetention - 1, listReplicatedVms(xapi, scheduleId, srUuid, vm.uuid))
47
47
 
48
48
  const deleteOldBackups = () => asyncMapSettled(oldVms, vm => xapi.VM_destroy(vm.$ref))
49
- const { deleteFirst } = settings
49
+ const { deleteFirst, _warmMigration } = settings
50
50
  if (deleteFirst) {
51
51
  await deleteOldBackups()
52
52
  }
@@ -55,14 +55,18 @@ exports.FullReplicationWriter = class FullReplicationWriter extends MixinReplica
55
55
  await Task.run({ name: 'transfer' }, async () => {
56
56
  targetVmRef = await xapi.VM_import(stream, sr.$ref, vm =>
57
57
  Promise.all([
58
- vm.add_tags('Disaster Recovery'),
59
- vm.ha_restart_priority !== '' && Promise.all([vm.set_ha_restart_priority(''), vm.add_tags('HA disabled')]),
58
+ !_warmMigration && vm.add_tags('Disaster Recovery'),
59
+ // warm migration does not disable HA , since the goal is to start the new VM in production
60
+ !_warmMigration &&
61
+ vm.ha_restart_priority !== '' &&
62
+ Promise.all([vm.set_ha_restart_priority(''), vm.add_tags('HA disabled')]),
60
63
  vm.set_name_label(`${vm.name_label} - ${job.name} - (${formatFilenameDate(timestamp)})`),
61
64
  ])
62
65
  )
63
66
  return { size: sizeContainer.size }
64
67
  })
65
68
 
69
+ this._targetVmRef = targetVmRef
66
70
  const targetVm = await xapi.getRecord('VM', targetVmRef)
67
71
 
68
72
  await Promise.all([
@@ -1,5 +1,17 @@
1
1
  'use strict'
2
2
 
3
+ const { Task } = require('../Task')
4
+ const assert = require('node:assert/strict')
5
+ const { HealthCheckVmBackup } = require('../HealthCheckVmBackup')
6
+
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
+ }
3
15
  exports.MixinReplicationWriter = (BaseClass = Object) =>
4
16
  class MixinReplicationWriter extends BaseClass {
5
17
  constructor({ sr, ...rest }) {
@@ -7,4 +19,32 @@ exports.MixinReplicationWriter = (BaseClass = Object) =>
7
19
 
8
20
  this._sr = sr
9
21
  }
22
+
23
+ healthCheck(sr) {
24
+ assert.notEqual(this._targetVmRef, undefined, 'A vm should have been transfered to be health checked')
25
+ // copy VM
26
+ return Task.run(
27
+ {
28
+ name: 'health check',
29
+ },
30
+ async () => {
31
+ const { $xapi: xapi } = sr
32
+ let clonedVm
33
+ try {
34
+ const baseVm = xapi.getObject(this._targetVmRef) ?? (await xapi.waitObject(this._targetVmRef))
35
+ const clonedRef = await xapi
36
+ .callAsync('VM.clone', this._targetVmRef, `Health Check - ${baseVm.name_label}`)
37
+ .then(extractOpaqueRef)
38
+ clonedVm = xapi.getObject(clonedRef) ?? (await xapi.waitObject(clonedRef))
39
+
40
+ await new HealthCheckVmBackup({
41
+ restoredVm: clonedVm,
42
+ xapi,
43
+ }).run()
44
+ } finally {
45
+ clonedVm && (await xapi.VM_destroy(clonedVm.$ref))
46
+ }
47
+ }
48
+ )
49
+ }
10
50
  }