@xen-orchestra/backups 0.36.1 → 0.37.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 +11 -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/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/FullXapi.js +61 -0
- package/_runners/_vmRunners/IncrementalXapi.js +163 -0
- package/_runners/_vmRunners/_Abstract.js +87 -0
- package/_runners/_vmRunners/_AbstractXapi.js +258 -0
- package/_runners/_vmRunners/_forkDeltaExport.js +12 -0
- package/{writers/FullBackupWriter.js → _runners/_writers/FullRemoteWriter.js} +5 -5
- package/{writers/FullReplicationWriter.js → _runners/_writers/FullXapiWriter.js} +5 -5
- package/{writers/DeltaBackupWriter.js → _runners/_writers/IncrementalRemoteWriter.js} +7 -7
- package/{writers/DeltaReplicationWriter.js → _runners/_writers/IncrementalXapiWriter.js} +8 -8
- package/{writers/_AbstractDeltaWriter.js → _runners/_writers/_AbstractIncrementalWriter.js} +1 -1
- package/{writers/_MixinBackupWriter.js → _runners/_writers/_MixinRemoteWriter.js} +10 -10
- package/{writers/_MixinReplicationWriter.js → _runners/_writers/_MixinXapiWriter.js} +6 -12
- package/package.json +5 -5
- package/_VmBackup.js +0 -515
- /package/{_createStreamThrottle.js → _runners/_createStreamThrottle.js} +0 -0
- /package/{_forkStreamUnpipe.js → _runners/_forkStreamUnpipe.js} +0 -0
- /package/{writers → _runners/_writers}/_AbstractFullWriter.js +0 -0
- /package/{writers → _runners/_writers}/_AbstractWriter.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
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const assert = require('assert')
|
|
4
|
+
const groupBy = require('lodash/groupBy.js')
|
|
5
|
+
const ignoreErrors = require('promise-toolbox/ignoreErrors')
|
|
6
|
+
const { asyncMap } = require('@xen-orchestra/async-map')
|
|
7
|
+
const { decorateMethodsWith } = require('@vates/decorate-with')
|
|
8
|
+
const { defer } = require('golike-defer')
|
|
9
|
+
const { formatDateTime } = require('@xen-orchestra/xapi')
|
|
10
|
+
|
|
11
|
+
const { getOldEntries } = require('../../_getOldEntries.js')
|
|
12
|
+
const { Task } = require('../../Task.js')
|
|
13
|
+
const { Abstract } = require('./_Abstract.js')
|
|
14
|
+
|
|
15
|
+
class AbstractXapiVmBackupRunner extends Abstract {
|
|
16
|
+
constructor({
|
|
17
|
+
config,
|
|
18
|
+
getSnapshotNameLabel,
|
|
19
|
+
healthCheckSr,
|
|
20
|
+
job,
|
|
21
|
+
remoteAdapters,
|
|
22
|
+
remotes,
|
|
23
|
+
schedule,
|
|
24
|
+
settings,
|
|
25
|
+
srs,
|
|
26
|
+
throttleStream,
|
|
27
|
+
vm,
|
|
28
|
+
}) {
|
|
29
|
+
super()
|
|
30
|
+
if (vm.other_config['xo:backup:job'] === job.id && 'start' in vm.blocked_operations) {
|
|
31
|
+
// don't match replicated VMs created by this very job otherwise they
|
|
32
|
+
// will be replicated again and again
|
|
33
|
+
throw new Error('cannot backup a VM created by this very job')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.config = config
|
|
37
|
+
this.job = job
|
|
38
|
+
this.remoteAdapters = remoteAdapters
|
|
39
|
+
this.scheduleId = schedule.id
|
|
40
|
+
this.timestamp = undefined
|
|
41
|
+
|
|
42
|
+
// VM currently backed up
|
|
43
|
+
this.vm = vm
|
|
44
|
+
const { tags } = this.vm
|
|
45
|
+
|
|
46
|
+
// VM (snapshot) that is really exported
|
|
47
|
+
this.exportedVm = undefined
|
|
48
|
+
|
|
49
|
+
this._fullVdisRequired = undefined
|
|
50
|
+
this._getSnapshotNameLabel = getSnapshotNameLabel
|
|
51
|
+
this._isIncremental = job.mode === 'delta'
|
|
52
|
+
this._healthCheckSr = healthCheckSr
|
|
53
|
+
this._jobId = job.id
|
|
54
|
+
this._jobSnapshots = undefined
|
|
55
|
+
this._throttleStream = throttleStream
|
|
56
|
+
this._xapi = vm.$xapi
|
|
57
|
+
|
|
58
|
+
// Base VM for the export
|
|
59
|
+
this._baseVm = undefined
|
|
60
|
+
|
|
61
|
+
// Settings for this specific run (job, schedule, VM)
|
|
62
|
+
if (tags.includes('xo-memory-backup')) {
|
|
63
|
+
settings.checkpointSnapshot = true
|
|
64
|
+
}
|
|
65
|
+
if (tags.includes('xo-offline-backup')) {
|
|
66
|
+
settings.offlineSnapshot = true
|
|
67
|
+
}
|
|
68
|
+
this._settings = settings
|
|
69
|
+
|
|
70
|
+
// Create writers
|
|
71
|
+
{
|
|
72
|
+
const writers = new Set()
|
|
73
|
+
this._writers = writers
|
|
74
|
+
|
|
75
|
+
const [BackupWriter, ReplicationWriter] = this._getWriters()
|
|
76
|
+
|
|
77
|
+
const allSettings = job.settings
|
|
78
|
+
Object.keys(remoteAdapters).forEach(remoteId => {
|
|
79
|
+
const targetSettings = {
|
|
80
|
+
...settings,
|
|
81
|
+
...allSettings[remoteId],
|
|
82
|
+
}
|
|
83
|
+
if (targetSettings.exportRetention !== 0) {
|
|
84
|
+
writers.add(new BackupWriter({ backup: this, remoteId, settings: targetSettings }))
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
srs.forEach(sr => {
|
|
88
|
+
const targetSettings = {
|
|
89
|
+
...settings,
|
|
90
|
+
...allSettings[sr.uuid],
|
|
91
|
+
}
|
|
92
|
+
if (targetSettings.copyRetention !== 0) {
|
|
93
|
+
writers.add(new ReplicationWriter({ backup: this, sr, settings: targetSettings }))
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ensure the VM itself does not have any backup metadata which would be
|
|
100
|
+
// copied on manual snapshots and interfere with the backup jobs
|
|
101
|
+
async _cleanMetadata() {
|
|
102
|
+
const { vm } = this
|
|
103
|
+
if ('xo:backup:job' in vm.other_config) {
|
|
104
|
+
await vm.update_other_config({
|
|
105
|
+
'xo:backup:datetime': null,
|
|
106
|
+
'xo:backup:deltaChainLength': null,
|
|
107
|
+
'xo:backup:exported': null,
|
|
108
|
+
'xo:backup:job': null,
|
|
109
|
+
'xo:backup:schedule': null,
|
|
110
|
+
'xo:backup:vm': null,
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async _snapshot() {
|
|
116
|
+
const { vm } = this
|
|
117
|
+
const xapi = this._xapi
|
|
118
|
+
|
|
119
|
+
const settings = this._settings
|
|
120
|
+
|
|
121
|
+
if (this._mustDoSnapshot()) {
|
|
122
|
+
await Task.run({ name: 'snapshot' }, async () => {
|
|
123
|
+
if (!settings.bypassVdiChainsCheck) {
|
|
124
|
+
await vm.$assertHealthyVdiChains()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const snapshotRef = await vm[settings.checkpointSnapshot ? '$checkpoint' : '$snapshot']({
|
|
128
|
+
ignoreNobakVdis: true,
|
|
129
|
+
name_label: this._getSnapshotNameLabel(vm),
|
|
130
|
+
unplugVusbs: true,
|
|
131
|
+
})
|
|
132
|
+
this.timestamp = Date.now()
|
|
133
|
+
|
|
134
|
+
await xapi.setFieldEntries('VM', snapshotRef, 'other_config', {
|
|
135
|
+
'xo:backup:datetime': formatDateTime(this.timestamp),
|
|
136
|
+
'xo:backup:job': this._jobId,
|
|
137
|
+
'xo:backup:schedule': this.scheduleId,
|
|
138
|
+
'xo:backup:vm': vm.uuid,
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
this.exportedVm = await xapi.getRecord('VM', snapshotRef)
|
|
142
|
+
|
|
143
|
+
return this.exportedVm.uuid
|
|
144
|
+
})
|
|
145
|
+
} else {
|
|
146
|
+
this.exportedVm = vm
|
|
147
|
+
this.timestamp = Date.now()
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async _fetchJobSnapshots() {
|
|
152
|
+
const jobId = this._jobId
|
|
153
|
+
const vmRef = this.vm.$ref
|
|
154
|
+
const xapi = this._xapi
|
|
155
|
+
|
|
156
|
+
const snapshotsRef = await xapi.getField('VM', vmRef, 'snapshots')
|
|
157
|
+
const snapshotsOtherConfig = await asyncMap(snapshotsRef, ref => xapi.getField('VM', ref, 'other_config'))
|
|
158
|
+
|
|
159
|
+
const snapshots = []
|
|
160
|
+
snapshotsOtherConfig.forEach((other_config, i) => {
|
|
161
|
+
if (other_config['xo:backup:job'] === jobId) {
|
|
162
|
+
snapshots.push({ other_config, $ref: snapshotsRef[i] })
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
snapshots.sort((a, b) => (a.other_config['xo:backup:datetime'] < b.other_config['xo:backup:datetime'] ? -1 : 1))
|
|
166
|
+
this._jobSnapshots = snapshots
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async _removeUnusedSnapshots() {
|
|
170
|
+
const allSettings = this.job.settings
|
|
171
|
+
const baseSettings = this._baseSettings
|
|
172
|
+
const baseVmRef = this._baseVm?.$ref
|
|
173
|
+
|
|
174
|
+
const snapshotsPerSchedule = groupBy(this._jobSnapshots, _ => _.other_config['xo:backup:schedule'])
|
|
175
|
+
const xapi = this._xapi
|
|
176
|
+
await asyncMap(Object.entries(snapshotsPerSchedule), ([scheduleId, snapshots]) => {
|
|
177
|
+
const settings = {
|
|
178
|
+
...baseSettings,
|
|
179
|
+
...allSettings[scheduleId],
|
|
180
|
+
...allSettings[this.vm.uuid],
|
|
181
|
+
}
|
|
182
|
+
return asyncMap(getOldEntries(settings.snapshotRetention, snapshots), ({ $ref }) => {
|
|
183
|
+
if ($ref !== baseVmRef) {
|
|
184
|
+
return xapi.VM_destroy($ref)
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async copy() {
|
|
191
|
+
throw new Error('Not implemented')
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
_getWriters() {
|
|
195
|
+
throw new Error('Not implemented')
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
_mustDoSnapshot() {
|
|
199
|
+
throw new Error('Not implemented')
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async _selectBaseVm() {
|
|
203
|
+
throw new Error('Not implemented')
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async run($defer) {
|
|
207
|
+
const settings = this._settings
|
|
208
|
+
assert(
|
|
209
|
+
!settings.offlineBackup || settings.snapshotRetention === 0,
|
|
210
|
+
'offlineBackup is not compatible with snapshotRetention'
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
await this._callWriters(async writer => {
|
|
214
|
+
await writer.beforeBackup()
|
|
215
|
+
$defer(async () => {
|
|
216
|
+
await writer.afterBackup()
|
|
217
|
+
})
|
|
218
|
+
}, 'writer.beforeBackup()')
|
|
219
|
+
|
|
220
|
+
await this._fetchJobSnapshots()
|
|
221
|
+
|
|
222
|
+
await this._selectBaseVm()
|
|
223
|
+
|
|
224
|
+
await this._cleanMetadata()
|
|
225
|
+
await this._removeUnusedSnapshots()
|
|
226
|
+
|
|
227
|
+
const { vm } = this
|
|
228
|
+
const isRunning = vm.power_state === 'Running'
|
|
229
|
+
const startAfter = isRunning && (settings.offlineBackup ? 'backup' : settings.offlineSnapshot && 'snapshot')
|
|
230
|
+
if (startAfter) {
|
|
231
|
+
await vm.$callAsync('clean_shutdown')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
await this._snapshot()
|
|
236
|
+
if (startAfter === 'snapshot') {
|
|
237
|
+
ignoreErrors.call(vm.$callAsync('start', false, false))
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (this._writers.size !== 0) {
|
|
241
|
+
await this._copy()
|
|
242
|
+
}
|
|
243
|
+
} finally {
|
|
244
|
+
if (startAfter) {
|
|
245
|
+
ignoreErrors.call(vm.$callAsync('start', false, false))
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
await this._fetchJobSnapshots()
|
|
249
|
+
await this._removeUnusedSnapshots()
|
|
250
|
+
}
|
|
251
|
+
await this._healthCheck()
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
exports.AbstractXapi = AbstractXapiVmBackupRunner
|
|
255
|
+
|
|
256
|
+
decorateMethodsWith(AbstractXapiVmBackupRunner, {
|
|
257
|
+
run: defer,
|
|
258
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { mapValues } = require('lodash')
|
|
4
|
+
const { forkStreamUnpipe } = require('../_forkStreamUnpipe')
|
|
5
|
+
|
|
6
|
+
exports.forkDeltaExport = function forkDeltaExport(deltaExport) {
|
|
7
|
+
return Object.create(deltaExport, {
|
|
8
|
+
streams: {
|
|
9
|
+
value: mapValues(deltaExport.streams, forkStreamUnpipe),
|
|
10
|
+
},
|
|
11
|
+
})
|
|
12
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { formatFilenameDate } = require('
|
|
4
|
-
const { getOldEntries } = require('
|
|
5
|
-
const { Task } = require('
|
|
3
|
+
const { formatFilenameDate } = require('../../_filenameDate.js')
|
|
4
|
+
const { getOldEntries } = require('../../_getOldEntries.js')
|
|
5
|
+
const { Task } = require('../../Task.js')
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const { MixinRemoteWriter } = require('./_MixinRemoteWriter.js')
|
|
8
8
|
const { AbstractFullWriter } = require('./_AbstractFullWriter.js')
|
|
9
9
|
|
|
10
|
-
exports.
|
|
10
|
+
exports.FullRemoteWriter = class FullRemoteWriter extends MixinRemoteWriter(AbstractFullWriter) {
|
|
11
11
|
constructor(props) {
|
|
12
12
|
super(props)
|
|
13
13
|
|
|
@@ -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
|
|
|
@@ -11,19 +11,19 @@ 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
29
|
const backup = this._backup
|
|
@@ -227,6 +227,6 @@ class DeltaBackupWriter extends MixinBackupWriter(AbstractDeltaWriter) {
|
|
|
227
227
|
// TODO: run cleanup?
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
|
-
exports.
|
|
230
|
+
exports.IncrementalRemoteWriter = decorateClass(IncrementalRemoteWriter, {
|
|
231
231
|
_transfer: defer,
|
|
232
232
|
})
|
|
@@ -4,16 +4,16 @@ 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
19
|
const replicatedVm = listReplicatedVms(sr.$xapi, this._backup.job.id, sr.uuid, this._backup.vm.uuid).find(
|
|
@@ -90,7 +90,7 @@ exports.DeltaReplicationWriter = class DeltaReplicationWriter extends MixinRepli
|
|
|
90
90
|
|
|
91
91
|
let targetVmRef
|
|
92
92
|
await Task.run({ name: 'transfer' }, async () => {
|
|
93
|
-
targetVmRef = await
|
|
93
|
+
targetVmRef = await importIncrementalVm(
|
|
94
94
|
{
|
|
95
95
|
__proto__: deltaExport,
|
|
96
96
|
vm: {
|
|
@@ -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
|
}
|
|
@@ -4,17 +4,17 @@ 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
20
|
constructor({ remoteId, ...rest }) {
|
|
@@ -58,7 +58,7 @@ exports.MixinBackupWriter = (BaseClass = Object) =>
|
|
|
58
58
|
const { disableMergeWorker } = this._backup.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,7 +71,7 @@ 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
|
}
|
|
@@ -1,19 +1,13 @@
|
|
|
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
|
|
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.37.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.4.
|
|
45
|
+
"vhd-lib": "^4.4.1",
|
|
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": {
|