@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
|
|
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.
|
|
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.
|
|
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",
|
package/runBackupWorker.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
}
|