@xen-orchestra/backups 0.36.1 → 0.38.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/Backup.js +14 -302
- package/ImportVmBackup.js +6 -6
- package/RemoteAdapter.js +20 -13
- package/RestoreMetadataBackup.js +1 -1
- package/_backupWorker.js +2 -2
- package/{_deltaVm.js → _incrementalVm.js} +11 -11
- package/_runners/Metadata.js +134 -0
- package/_runners/VmsRemote.js +98 -0
- package/_runners/VmsXapi.js +138 -0
- package/_runners/_Abstract.js +51 -0
- package/{_PoolMetadataBackup.js → _runners/_PoolMetadataBackup.js} +3 -3
- package/_runners/_RemoteTimeoutError.js +8 -0
- package/{_XoMetadataBackup.js → _runners/_XoMetadataBackup.js} +3 -3
- package/_runners/_getAdaptersByRemote.js +9 -0
- package/_runners/_runTask.js +6 -0
- package/_runners/_vmRunners/FullRemote.js +53 -0
- package/_runners/_vmRunners/FullXapi.js +65 -0
- package/_runners/_vmRunners/IncrementalRemote.js +67 -0
- package/_runners/_vmRunners/IncrementalXapi.js +175 -0
- package/_runners/_vmRunners/_Abstract.js +95 -0
- package/_runners/_vmRunners/_AbstractRemote.js +86 -0
- package/_runners/_vmRunners/_AbstractXapi.js +257 -0
- package/_runners/_vmRunners/_forkDeltaExport.js +12 -0
- package/{writers/FullBackupWriter.js → _runners/_writers/FullRemoteWriter.js} +15 -13
- package/{writers/FullReplicationWriter.js → _runners/_writers/FullXapiWriter.js} +8 -7
- package/{writers/DeltaBackupWriter.js → _runners/_writers/IncrementalRemoteWriter.js} +28 -22
- package/{writers/DeltaReplicationWriter.js → _runners/_writers/IncrementalXapiWriter.js} +19 -16
- package/{writers → _runners/_writers}/_AbstractFullWriter.js +2 -2
- package/{writers/_AbstractDeltaWriter.js → _runners/_writers/_AbstractIncrementalWriter.js} +3 -3
- package/_runners/_writers/_AbstractWriter.js +31 -0
- package/{writers/_MixinBackupWriter.js → _runners/_writers/_MixinRemoteWriter.js} +30 -16
- package/{writers/_MixinReplicationWriter.js → _runners/_writers/_MixinXapiWriter.js} +9 -13
- package/package.json +5 -5
- package/_VmBackup.js +0 -515
- package/writers/_AbstractWriter.js +0 -14
- /package/{_createStreamThrottle.js → _runners/_createStreamThrottle.js} +0 -0
- /package/{_forkStreamUnpipe.js → _runners/_forkStreamUnpipe.js} +0 -0
- /package/{writers → _runners/_writers}/_checkVhd.js +0 -0
- /package/{writers → _runners/_writers}/_listReplicatedVms.js +0 -0
- /package/{writers → _runners/_writers}/_packUuid.js +0 -0
|
@@ -4,15 +4,15 @@ const ignoreErrors = require('promise-toolbox/ignoreErrors')
|
|
|
4
4
|
const { asyncMap, asyncMapSettled } = require('@xen-orchestra/async-map')
|
|
5
5
|
const { formatDateTime } = require('@xen-orchestra/xapi')
|
|
6
6
|
|
|
7
|
-
const { formatFilenameDate } = require('
|
|
8
|
-
const { getOldEntries } = require('
|
|
9
|
-
const { Task } = require('
|
|
7
|
+
const { formatFilenameDate } = require('../../_filenameDate.js')
|
|
8
|
+
const { getOldEntries } = require('../../_getOldEntries.js')
|
|
9
|
+
const { Task } = require('../../Task.js')
|
|
10
10
|
|
|
11
11
|
const { AbstractFullWriter } = require('./_AbstractFullWriter.js')
|
|
12
|
-
const {
|
|
12
|
+
const { MixinXapiWriter } = require('./_MixinXapiWriter.js')
|
|
13
13
|
const { listReplicatedVms } = require('./_listReplicatedVms.js')
|
|
14
14
|
|
|
15
|
-
exports.
|
|
15
|
+
exports.FullXapiWriter = class FullXapiWriter extends MixinXapiWriter(AbstractFullWriter) {
|
|
16
16
|
constructor(props) {
|
|
17
17
|
super(props)
|
|
18
18
|
|
|
@@ -32,10 +32,11 @@ exports.FullReplicationWriter = class FullReplicationWriter extends MixinReplica
|
|
|
32
32
|
)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
async _run({ timestamp, sizeContainer, stream }) {
|
|
35
|
+
async _run({ timestamp, sizeContainer, stream, vm }) {
|
|
36
36
|
const sr = this._sr
|
|
37
37
|
const settings = this._settings
|
|
38
|
-
const
|
|
38
|
+
const job = this._job
|
|
39
|
+
const scheduleId = this.scheduleId
|
|
39
40
|
|
|
40
41
|
const { uuid: srUuid, $xapi: xapi } = sr
|
|
41
42
|
|
|
@@ -11,25 +11,24 @@ const { decorateClass } = require('@vates/decorate-with')
|
|
|
11
11
|
const { defer } = require('golike-defer')
|
|
12
12
|
const { dirname } = require('path')
|
|
13
13
|
|
|
14
|
-
const { formatFilenameDate } = require('
|
|
15
|
-
const { getOldEntries } = require('
|
|
16
|
-
const { Task } = require('
|
|
14
|
+
const { formatFilenameDate } = require('../../_filenameDate.js')
|
|
15
|
+
const { getOldEntries } = require('../../_getOldEntries.js')
|
|
16
|
+
const { Task } = require('../../Task.js')
|
|
17
17
|
|
|
18
|
-
const {
|
|
19
|
-
const {
|
|
18
|
+
const { MixinRemoteWriter } = require('./_MixinRemoteWriter.js')
|
|
19
|
+
const { AbstractIncrementalWriter } = require('./_AbstractIncrementalWriter.js')
|
|
20
20
|
const { checkVhd } = require('./_checkVhd.js')
|
|
21
21
|
const { packUuid } = require('./_packUuid.js')
|
|
22
22
|
const { Disposable } = require('promise-toolbox')
|
|
23
23
|
|
|
24
24
|
const { warn } = createLogger('xo:backups:DeltaBackupWriter')
|
|
25
25
|
|
|
26
|
-
class
|
|
26
|
+
class IncrementalRemoteWriter extends MixinRemoteWriter(AbstractIncrementalWriter) {
|
|
27
27
|
async checkBaseVdis(baseUuidToSrcVdi) {
|
|
28
28
|
const { handler } = this._adapter
|
|
29
|
-
const backup = this._backup
|
|
30
29
|
const adapter = this._adapter
|
|
31
30
|
|
|
32
|
-
const vdisDir = `${this._vmBackupDir}/vdis/${
|
|
31
|
+
const vdisDir = `${this._vmBackupDir}/vdis/${this._job.id}`
|
|
33
32
|
|
|
34
33
|
await asyncMap(baseUuidToSrcVdi, async ([baseUuid, srcVdi]) => {
|
|
35
34
|
let found = false
|
|
@@ -91,11 +90,12 @@ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
|
|
|
91
90
|
async _prepare() {
|
|
92
91
|
const adapter = this._adapter
|
|
93
92
|
const settings = this._settings
|
|
94
|
-
const
|
|
93
|
+
const scheduleId = this._scheduleId
|
|
94
|
+
const vmUuid = this._vmUuid
|
|
95
95
|
|
|
96
96
|
const oldEntries = getOldEntries(
|
|
97
97
|
settings.exportRetention - 1,
|
|
98
|
-
await adapter.listVmBackups(
|
|
98
|
+
await adapter.listVmBackups(vmUuid, _ => _.mode === 'delta' && _.scheduleId === scheduleId)
|
|
99
99
|
)
|
|
100
100
|
this._oldEntries = oldEntries
|
|
101
101
|
|
|
@@ -134,16 +134,19 @@ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
async _transfer($defer, { timestamp, deltaExport }) {
|
|
137
|
+
async _transfer($defer, { differentialVhds, timestamp, deltaExport, vm, vmSnapshot }) {
|
|
138
138
|
const adapter = this._adapter
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
const { job, scheduleId, vm } = backup
|
|
139
|
+
const job = this._job
|
|
140
|
+
const scheduleId = this._scheduleId
|
|
142
141
|
|
|
143
142
|
const jobId = job.id
|
|
144
143
|
const handler = adapter.handler
|
|
145
144
|
|
|
146
|
-
|
|
145
|
+
let metadataContent = await this._isAlreadyTransferred(timestamp)
|
|
146
|
+
if (metadataContent !== undefined) {
|
|
147
|
+
// @todo : should skip backup while being vigilant to not stuck the forked stream
|
|
148
|
+
Task.info('This backup has already been transfered')
|
|
149
|
+
}
|
|
147
150
|
|
|
148
151
|
const basename = formatFilenameDate(timestamp)
|
|
149
152
|
const vhds = mapValues(
|
|
@@ -158,7 +161,7 @@ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
|
|
|
158
161
|
}/${adapter.getVhdFileName(basename)}`
|
|
159
162
|
)
|
|
160
163
|
|
|
161
|
-
|
|
164
|
+
metadataContent = {
|
|
162
165
|
jobId,
|
|
163
166
|
mode: job.mode,
|
|
164
167
|
scheduleId,
|
|
@@ -169,16 +172,15 @@ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
|
|
|
169
172
|
vifs: deltaExport.vifs,
|
|
170
173
|
vhds,
|
|
171
174
|
vm,
|
|
172
|
-
vmSnapshot
|
|
175
|
+
vmSnapshot,
|
|
173
176
|
}
|
|
174
|
-
|
|
175
177
|
const { size } = await Task.run({ name: 'transfer' }, async () => {
|
|
176
178
|
let transferSize = 0
|
|
177
179
|
await Promise.all(
|
|
178
180
|
map(deltaExport.vdis, async (vdi, id) => {
|
|
179
181
|
const path = `${this._vmBackupDir}/${vhds[id]}`
|
|
180
182
|
|
|
181
|
-
const isDelta =
|
|
183
|
+
const isDelta = differentialVhds[`${id}.vhd`]
|
|
182
184
|
let parentPath
|
|
183
185
|
if (isDelta) {
|
|
184
186
|
const vdiDir = dirname(path)
|
|
@@ -191,7 +193,11 @@ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
|
|
|
191
193
|
.sort()
|
|
192
194
|
.pop()
|
|
193
195
|
|
|
194
|
-
assert.notStrictEqual(
|
|
196
|
+
assert.notStrictEqual(
|
|
197
|
+
parentPath,
|
|
198
|
+
undefined,
|
|
199
|
+
`missing parent of ${id} in ${dirname(path)}, looking for ${vdi.other_config['xo:base_delta']}`
|
|
200
|
+
)
|
|
195
201
|
|
|
196
202
|
parentPath = parentPath.slice(1) // remove leading slash
|
|
197
203
|
|
|
@@ -204,7 +210,7 @@ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
|
|
|
204
210
|
// merges and chainings
|
|
205
211
|
checksum: false,
|
|
206
212
|
validator: tmpPath => checkVhd(handler, tmpPath),
|
|
207
|
-
writeBlockConcurrency: this.
|
|
213
|
+
writeBlockConcurrency: this._config.writeBlockConcurrency,
|
|
208
214
|
})
|
|
209
215
|
|
|
210
216
|
if (isDelta) {
|
|
@@ -227,6 +233,6 @@ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
|
|
|
227
233
|
// TODO: run cleanup?
|
|
228
234
|
}
|
|
229
235
|
}
|
|
230
|
-
exports.
|
|
236
|
+
exports.IncrementalRemoteWriter = decorateClass(IncrementalRemoteWriter, {
|
|
231
237
|
_transfer: defer,
|
|
232
238
|
})
|
|
@@ -4,19 +4,19 @@ const { asyncMap, asyncMapSettled } = require('@xen-orchestra/async-map')
|
|
|
4
4
|
const ignoreErrors = require('promise-toolbox/ignoreErrors')
|
|
5
5
|
const { formatDateTime } = require('@xen-orchestra/xapi')
|
|
6
6
|
|
|
7
|
-
const { formatFilenameDate } = require('
|
|
8
|
-
const { getOldEntries } = require('
|
|
9
|
-
const {
|
|
10
|
-
const { Task } = require('
|
|
7
|
+
const { formatFilenameDate } = require('../../_filenameDate.js')
|
|
8
|
+
const { getOldEntries } = require('../../_getOldEntries.js')
|
|
9
|
+
const { importIncrementalVm, TAG_COPY_SRC } = require('../../_incrementalVm.js')
|
|
10
|
+
const { Task } = require('../../Task.js')
|
|
11
11
|
|
|
12
|
-
const {
|
|
13
|
-
const {
|
|
12
|
+
const { AbstractIncrementalWriter } = require('./_AbstractIncrementalWriter.js')
|
|
13
|
+
const { MixinXapiWriter } = require('./_MixinXapiWriter.js')
|
|
14
14
|
const { listReplicatedVms } = require('./_listReplicatedVms.js')
|
|
15
15
|
|
|
16
|
-
exports.
|
|
16
|
+
exports.IncrementalXapiWriter = class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWriter) {
|
|
17
17
|
async checkBaseVdis(baseUuidToSrcVdi, baseVm) {
|
|
18
18
|
const sr = this._sr
|
|
19
|
-
const replicatedVm = listReplicatedVms(sr.$xapi, this.
|
|
19
|
+
const replicatedVm = listReplicatedVms(sr.$xapi, this._job.id, sr.uuid, this._vmUuid).find(
|
|
20
20
|
vm => vm.other_config[TAG_COPY_SRC] === baseVm.uuid
|
|
21
21
|
)
|
|
22
22
|
if (replicatedVm === undefined) {
|
|
@@ -49,9 +49,10 @@ exports.DeltaReplicationWriter = class DeltaReplicationWriter extends MixinRepli
|
|
|
49
49
|
type: 'SR',
|
|
50
50
|
},
|
|
51
51
|
})
|
|
52
|
+
const hasHealthCheckSr = this._healthCheckSr !== undefined
|
|
52
53
|
this.transfer = task.wrapFn(this.transfer)
|
|
53
|
-
this.cleanup = task.wrapFn(this.cleanup)
|
|
54
|
-
this.healthCheck = task.wrapFn(this.healthCheck,
|
|
54
|
+
this.cleanup = task.wrapFn(this.cleanup, !hasHealthCheckSr)
|
|
55
|
+
this.healthCheck = task.wrapFn(this.healthCheck, hasHealthCheckSr)
|
|
55
56
|
|
|
56
57
|
return task.run(() => this._prepare())
|
|
57
58
|
}
|
|
@@ -59,12 +60,13 @@ exports.DeltaReplicationWriter = class DeltaReplicationWriter extends MixinRepli
|
|
|
59
60
|
async _prepare() {
|
|
60
61
|
const settings = this._settings
|
|
61
62
|
const { uuid: srUuid, $xapi: xapi } = this._sr
|
|
62
|
-
const
|
|
63
|
+
const vmUuid = this._vmUuid
|
|
64
|
+
const scheduleId = this._scheduleId
|
|
63
65
|
|
|
64
66
|
// delete previous interrupted copies
|
|
65
|
-
ignoreErrors.call(asyncMapSettled(listReplicatedVms(xapi, scheduleId, undefined,
|
|
67
|
+
ignoreErrors.call(asyncMapSettled(listReplicatedVms(xapi, scheduleId, undefined, vmUuid), vm => vm.$destroy))
|
|
66
68
|
|
|
67
|
-
this._oldEntries = getOldEntries(settings.copyRetention - 1, listReplicatedVms(xapi, scheduleId, srUuid,
|
|
69
|
+
this._oldEntries = getOldEntries(settings.copyRetention - 1, listReplicatedVms(xapi, scheduleId, srUuid, vmUuid))
|
|
68
70
|
|
|
69
71
|
if (settings.deleteFirst) {
|
|
70
72
|
await this._deleteOldEntries()
|
|
@@ -81,16 +83,17 @@ exports.DeltaReplicationWriter = class DeltaReplicationWriter extends MixinRepli
|
|
|
81
83
|
return asyncMapSettled(this._oldEntries, vm => vm.$destroy())
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
async _transfer({ timestamp, deltaExport, sizeContainers }) {
|
|
86
|
+
async _transfer({ timestamp, deltaExport, sizeContainers, vm }) {
|
|
85
87
|
const { _warmMigration } = this._settings
|
|
86
88
|
const sr = this._sr
|
|
87
|
-
const
|
|
89
|
+
const job = this._job
|
|
90
|
+
const scheduleId = this._scheduleId
|
|
88
91
|
|
|
89
92
|
const { uuid: srUuid, $xapi: xapi } = sr
|
|
90
93
|
|
|
91
94
|
let targetVmRef
|
|
92
95
|
await Task.run({ name: 'transfer' }, async () => {
|
|
93
|
-
targetVmRef = await
|
|
96
|
+
targetVmRef = await importIncrementalVm(
|
|
94
97
|
{
|
|
95
98
|
__proto__: deltaExport,
|
|
96
99
|
vm: {
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
const { AbstractWriter } = require('./_AbstractWriter.js')
|
|
4
4
|
|
|
5
5
|
exports.AbstractFullWriter = class AbstractFullWriter extends AbstractWriter {
|
|
6
|
-
async run({ timestamp, sizeContainer, stream }) {
|
|
6
|
+
async run({ timestamp, sizeContainer, stream, vm, vmSnapshot }) {
|
|
7
7
|
try {
|
|
8
|
-
return await this._run({ timestamp, sizeContainer, stream })
|
|
8
|
+
return await this._run({ timestamp, sizeContainer, stream, vm, vmSnapshot })
|
|
9
9
|
} finally {
|
|
10
10
|
// ensure stream is properly closed
|
|
11
11
|
stream.destroy()
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { AbstractWriter } = require('./_AbstractWriter.js')
|
|
4
4
|
|
|
5
|
-
exports.
|
|
5
|
+
exports.AbstractIncrementalWriter = class AbstractIncrementalWriter extends AbstractWriter {
|
|
6
6
|
checkBaseVdis(baseUuidToSrcVdi, baseVm) {
|
|
7
7
|
throw new Error('Not implemented')
|
|
8
8
|
}
|
|
@@ -15,9 +15,9 @@ exports.AbstractDeltaWriter = class AbstractDeltaWriter extends AbstractWriter {
|
|
|
15
15
|
throw new Error('Not implemented')
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
async transfer({
|
|
18
|
+
async transfer({ deltaExport, ...other }) {
|
|
19
19
|
try {
|
|
20
|
-
return await this._transfer({
|
|
20
|
+
return await this._transfer({ deltaExport, ...other })
|
|
21
21
|
} finally {
|
|
22
22
|
// ensure all streams are properly closed
|
|
23
23
|
for (const stream of Object.values(deltaExport.streams)) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { formatFilenameDate } = require('../../_filenameDate')
|
|
4
|
+
const { getVmBackupDir } = require('../../_getVmBackupDir')
|
|
5
|
+
|
|
6
|
+
exports.AbstractWriter = class AbstractWriter {
|
|
7
|
+
constructor({ config, healthCheckSr, job, vmUuid, scheduleId, settings }) {
|
|
8
|
+
this._config = config
|
|
9
|
+
this._healthCheckSr = healthCheckSr
|
|
10
|
+
this._job = job
|
|
11
|
+
this._scheduleId = scheduleId
|
|
12
|
+
this._settings = settings
|
|
13
|
+
this._vmUuid = vmUuid
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
beforeBackup() {}
|
|
17
|
+
|
|
18
|
+
afterBackup() {}
|
|
19
|
+
|
|
20
|
+
healthCheck(sr) {}
|
|
21
|
+
|
|
22
|
+
_isAlreadyTransferred(timestamp) {
|
|
23
|
+
const vmUuid = this._vmUuid
|
|
24
|
+
const adapter = this._adapter
|
|
25
|
+
const backupDir = getVmBackupDir(vmUuid)
|
|
26
|
+
try {
|
|
27
|
+
const actualMetadata = JSON.parse(adapter._handler.readFile(`${backupDir}/${formatFilenameDate(timestamp)}.json`))
|
|
28
|
+
return actualMetadata
|
|
29
|
+
} catch (error) {}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -4,26 +4,26 @@ const { createLogger } = require('@xen-orchestra/log')
|
|
|
4
4
|
const { join } = require('path')
|
|
5
5
|
|
|
6
6
|
const assert = require('assert')
|
|
7
|
-
const { formatFilenameDate } = require('
|
|
8
|
-
const { getVmBackupDir } = require('
|
|
9
|
-
const { HealthCheckVmBackup } = require('
|
|
10
|
-
const { ImportVmBackup } = require('
|
|
11
|
-
const { Task } = require('
|
|
12
|
-
const MergeWorker = require('
|
|
7
|
+
const { formatFilenameDate } = require('../../_filenameDate.js')
|
|
8
|
+
const { getVmBackupDir } = require('../../_getVmBackupDir.js')
|
|
9
|
+
const { HealthCheckVmBackup } = require('../../HealthCheckVmBackup.js')
|
|
10
|
+
const { ImportVmBackup } = require('../../ImportVmBackup.js')
|
|
11
|
+
const { Task } = require('../../Task.js')
|
|
12
|
+
const MergeWorker = require('../../merge-worker/index.js')
|
|
13
13
|
|
|
14
14
|
const { info, warn } = createLogger('xo:backups:MixinBackupWriter')
|
|
15
15
|
|
|
16
|
-
exports.
|
|
17
|
-
class
|
|
16
|
+
exports.MixinRemoteWriter = (BaseClass = Object) =>
|
|
17
|
+
class MixinRemoteWriter extends BaseClass {
|
|
18
18
|
#lock
|
|
19
19
|
|
|
20
|
-
constructor({ remoteId, ...rest }) {
|
|
20
|
+
constructor({ remoteId, adapter, ...rest }) {
|
|
21
21
|
super(rest)
|
|
22
22
|
|
|
23
|
-
this._adapter =
|
|
23
|
+
this._adapter = adapter
|
|
24
24
|
this._remoteId = remoteId
|
|
25
25
|
|
|
26
|
-
this._vmBackupDir = getVmBackupDir(
|
|
26
|
+
this._vmBackupDir = getVmBackupDir(rest.vmUuid)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async _cleanVm(options) {
|
|
@@ -38,7 +38,7 @@ exports.MixinBackupWriter = (BaseClass = Object) =>
|
|
|
38
38
|
Task.warning(message, data)
|
|
39
39
|
},
|
|
40
40
|
lock: false,
|
|
41
|
-
mergeBlockConcurrency: this.
|
|
41
|
+
mergeBlockConcurrency: this._config.mergeBlockConcurrency,
|
|
42
42
|
})
|
|
43
43
|
})
|
|
44
44
|
} catch (error) {
|
|
@@ -55,10 +55,10 @@ exports.MixinBackupWriter = (BaseClass = Object) =>
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
async afterBackup() {
|
|
58
|
-
const { disableMergeWorker } = this.
|
|
58
|
+
const { disableMergeWorker } = this._config
|
|
59
59
|
// merge worker only compatible with local remotes
|
|
60
60
|
const { handler } = this._adapter
|
|
61
|
-
const willMergeInWorker = !disableMergeWorker && typeof handler.
|
|
61
|
+
const willMergeInWorker = !disableMergeWorker && typeof handler.getRealPath === 'function'
|
|
62
62
|
|
|
63
63
|
const { merge } = await this._cleanVm({ remove: true, merge: !willMergeInWorker })
|
|
64
64
|
await this.#lock.dispose()
|
|
@@ -71,12 +71,14 @@ exports.MixinBackupWriter = (BaseClass = Object) =>
|
|
|
71
71
|
Math.random().toString(36).slice(2)
|
|
72
72
|
|
|
73
73
|
await handler.outputFile(taskFile, this._backup.vm.uuid)
|
|
74
|
-
const remotePath = handler.
|
|
74
|
+
const remotePath = handler.getRealPath()
|
|
75
75
|
await MergeWorker.run(remotePath)
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
healthCheck(
|
|
79
|
+
healthCheck() {
|
|
80
|
+
const sr = this._healthCheckSr
|
|
81
|
+
assert.notStrictEqual(sr, undefined, 'SR should be defined before making a health check')
|
|
80
82
|
assert.notStrictEqual(
|
|
81
83
|
this._metadataFileName,
|
|
82
84
|
undefined,
|
|
@@ -109,4 +111,16 @@ exports.MixinBackupWriter = (BaseClass = Object) =>
|
|
|
109
111
|
}
|
|
110
112
|
)
|
|
111
113
|
}
|
|
114
|
+
|
|
115
|
+
_isAlreadyTransferred(timestamp) {
|
|
116
|
+
const vmUuid = this._vmUuid
|
|
117
|
+
const adapter = this._adapter
|
|
118
|
+
const backupDir = getVmBackupDir(vmUuid)
|
|
119
|
+
try {
|
|
120
|
+
const actualMetadata = JSON.parse(
|
|
121
|
+
adapter._handler.readFile(`${backupDir}/${formatFilenameDate(timestamp)}.json`)
|
|
122
|
+
)
|
|
123
|
+
return actualMetadata
|
|
124
|
+
} catch (error) {}
|
|
125
|
+
}
|
|
112
126
|
}
|
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { extractOpaqueRef } = require('@xen-orchestra/xapi')
|
|
4
|
+
|
|
5
|
+
const { Task } = require('../../Task')
|
|
4
6
|
const assert = require('node:assert/strict')
|
|
5
|
-
const { HealthCheckVmBackup } = require('
|
|
7
|
+
const { HealthCheckVmBackup } = require('../../HealthCheckVmBackup')
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
}
|
|
15
|
-
exports.MixinReplicationWriter = (BaseClass = Object) =>
|
|
16
|
-
class MixinReplicationWriter extends BaseClass {
|
|
9
|
+
exports.MixinXapiWriter = (BaseClass = Object) =>
|
|
10
|
+
class MixinXapiWriter extends BaseClass {
|
|
17
11
|
constructor({ sr, ...rest }) {
|
|
18
12
|
super(rest)
|
|
19
13
|
|
|
20
14
|
this._sr = sr
|
|
21
15
|
}
|
|
22
16
|
|
|
23
|
-
healthCheck(
|
|
17
|
+
healthCheck() {
|
|
18
|
+
const sr = this._healthCheckSr
|
|
19
|
+
assert.notStrictEqual(sr, undefined, 'SR should be defined before making a health check')
|
|
24
20
|
assert.notEqual(this._targetVmRef, undefined, 'A vm should have been transfered to be health checked')
|
|
25
21
|
// copy VM
|
|
26
22
|
return Task.run(
|
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.38.0",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=14.6"
|
|
14
14
|
},
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@vates/nbd-client": "^1.2.0",
|
|
28
28
|
"@vates/parse-duration": "^0.1.1",
|
|
29
29
|
"@xen-orchestra/async-map": "^0.1.2",
|
|
30
|
-
"@xen-orchestra/fs": "^
|
|
30
|
+
"@xen-orchestra/fs": "^4.0.0",
|
|
31
31
|
"@xen-orchestra/log": "^0.6.0",
|
|
32
32
|
"@xen-orchestra/template": "^0.1.0",
|
|
33
33
|
"compare-versions": "^5.0.1",
|
|
@@ -42,17 +42,17 @@
|
|
|
42
42
|
"promise-toolbox": "^0.21.0",
|
|
43
43
|
"proper-lockfile": "^4.1.2",
|
|
44
44
|
"uuid": "^9.0.0",
|
|
45
|
-
"vhd-lib": "^4.
|
|
45
|
+
"vhd-lib": "^4.5.0",
|
|
46
46
|
"yazl": "^2.5.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"rimraf": "^
|
|
49
|
+
"rimraf": "^5.0.1",
|
|
50
50
|
"sinon": "^15.0.1",
|
|
51
51
|
"test": "^3.2.1",
|
|
52
52
|
"tmp": "^0.2.1"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"@xen-orchestra/xapi": "^2.2.
|
|
55
|
+
"@xen-orchestra/xapi": "^2.2.1"
|
|
56
56
|
},
|
|
57
57
|
"license": "AGPL-3.0-or-later",
|
|
58
58
|
"author": {
|