@xen-orchestra/backups 0.68.0 → 0.68.1
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/RemoteAdapter.mjs
CHANGED
|
@@ -613,6 +613,11 @@ export class RemoteAdapter {
|
|
|
613
613
|
// if cache is missing or broken => regenerate it and return
|
|
614
614
|
|
|
615
615
|
async _readCacheListVmBackups(vmUuid) {
|
|
616
|
+
// immutable remote can't use any caching
|
|
617
|
+
// since the cache file may be non modifiable
|
|
618
|
+
if (this._handler.isImmutable()) {
|
|
619
|
+
return this.#getCacheableDataListVmBackups(`${BACKUP_DIR}/${vmUuid}`)
|
|
620
|
+
}
|
|
616
621
|
const path = this.#getVmBackupsCache(vmUuid)
|
|
617
622
|
|
|
618
623
|
const cache = await this._readCache(path)
|
package/_incrementalVm.mjs
CHANGED
|
@@ -291,10 +291,16 @@ export const importIncrementalVm = defer(async function importIncrementalVm(
|
|
|
291
291
|
await xapi.VTPM_create({ VM: vmRef, contents })
|
|
292
292
|
})
|
|
293
293
|
)
|
|
294
|
-
|
|
294
|
+
const vm = await xapi.getRecord('VM', vmRef)
|
|
295
295
|
await Promise.all([
|
|
296
|
-
|
|
297
|
-
xapi.setField('VM', vmRef, 'name_label',
|
|
296
|
+
vmRecord.ha_always_run && xapi.setField('VM', vmRef, 'ha_always_run', true),
|
|
297
|
+
xapi.setField('VM', vmRef, 'name_label', vmRecord.name_label),
|
|
298
|
+
// correctly unlock the VM and reapply the target blocked operations
|
|
299
|
+
vm.update_blocked_operations({
|
|
300
|
+
start: null,
|
|
301
|
+
start_on: null,
|
|
302
|
+
...vmRecord.blocked_operations,
|
|
303
|
+
}),
|
|
298
304
|
])
|
|
299
305
|
|
|
300
306
|
return vmRef
|
|
@@ -3,6 +3,7 @@ import groupBy from 'lodash/groupBy.js'
|
|
|
3
3
|
import { createLogger } from '@xen-orchestra/log'
|
|
4
4
|
import ignoreErrors from 'promise-toolbox/ignoreErrors'
|
|
5
5
|
import { asyncMap } from '@xen-orchestra/async-map'
|
|
6
|
+
import { asyncEach } from '@vates/async-each'
|
|
6
7
|
import { decorateMethodsWith } from '@vates/decorate-with'
|
|
7
8
|
import { defer } from 'golike-defer'
|
|
8
9
|
|
|
@@ -13,6 +14,8 @@ import { DATETIME, JOB_ID, SCHEDULE_ID, VM_UUID, resetVmOtherConfig, setVmOtherC
|
|
|
13
14
|
|
|
14
15
|
const { warn, info } = createLogger('xo:backups:AbstractXapi')
|
|
15
16
|
|
|
17
|
+
const TEMP_SNAPSHOT_NAME = 'xo-backup-temp-snapshot-name'
|
|
18
|
+
|
|
16
19
|
export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
17
20
|
constructor({
|
|
18
21
|
config,
|
|
@@ -156,7 +159,7 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
|
156
159
|
|
|
157
160
|
const snapshotRef = await vm[settings.checkpointSnapshot ? '$checkpoint' : '$snapshot']({
|
|
158
161
|
ignoredVdisTag: '[NOBAK]',
|
|
159
|
-
name_label:
|
|
162
|
+
name_label: TEMP_SNAPSHOT_NAME,
|
|
160
163
|
unplugVusbs: true,
|
|
161
164
|
})
|
|
162
165
|
this.timestamp = Date.now()
|
|
@@ -166,6 +169,9 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
|
166
169
|
scheduleId: this.scheduleId,
|
|
167
170
|
vmUuid: vm.uuid,
|
|
168
171
|
})
|
|
172
|
+
const snapshot = await xapi.getRecord('VM', snapshotRef)
|
|
173
|
+
await snapshot.set_name_label(this._getSnapshotNameLabel(vm))
|
|
174
|
+
// reload data to ensure it is up to date with the new name label
|
|
169
175
|
this._exportedVm = await xapi.getRecord('VM', snapshotRef)
|
|
170
176
|
return this._exportedVm.uuid
|
|
171
177
|
})
|
|
@@ -371,6 +377,11 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
|
371
377
|
}
|
|
372
378
|
})
|
|
373
379
|
})
|
|
380
|
+
|
|
381
|
+
// list and remove the snapshot were the jobs failed between
|
|
382
|
+
// makesnapshot and update_other_config
|
|
383
|
+
const snapshots = this._vm.$snapshots.filter(_ => !!_).filter(({ name_label }) => name_label === TEMP_SNAPSHOT_NAME)
|
|
384
|
+
await asyncEach(snapshots, snapshot => snapshot.$destroy())
|
|
374
385
|
}
|
|
375
386
|
|
|
376
387
|
async _removeSnapshotData() {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { asyncMapSettled } from '@xen-orchestra/async-map'
|
|
2
2
|
import ignoreErrors from 'promise-toolbox/ignoreErrors'
|
|
3
3
|
|
|
4
|
-
import { formatFilenameDate } from '../../_filenameDate.mjs'
|
|
5
4
|
import { getOldEntries } from '../../_getOldEntries.mjs'
|
|
6
5
|
import { importIncrementalVm } from '../../_incrementalVm.mjs'
|
|
7
6
|
import { Task } from '../../Task.mjs'
|
|
@@ -9,8 +8,18 @@ import { Task } from '../../Task.mjs'
|
|
|
9
8
|
import { AbstractIncrementalWriter } from './_AbstractIncrementalWriter.mjs'
|
|
10
9
|
import { MixinXapiWriter } from './_MixinXapiWriter.mjs'
|
|
11
10
|
import { listReplicatedVms } from './_listReplicatedVms.mjs'
|
|
12
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
COPY_OF,
|
|
13
|
+
setVmOtherConfig,
|
|
14
|
+
BASE_DELTA_VDI,
|
|
15
|
+
JOB_ID,
|
|
16
|
+
SCHEDULE_ID,
|
|
17
|
+
REPLICATED_TO_SR_UUID,
|
|
18
|
+
DATETIME,
|
|
19
|
+
VM_UUID,
|
|
20
|
+
} from '../../_otherConfig.mjs'
|
|
13
21
|
import assert from 'node:assert'
|
|
22
|
+
import { formatFilenameDate } from '../../_filenameDate.mjs'
|
|
14
23
|
|
|
15
24
|
export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWriter) {
|
|
16
25
|
async checkBaseVdis(baseUuidToSrcVdi) {
|
|
@@ -94,12 +103,28 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
|
|
|
94
103
|
return asyncMapSettled(this._oldEntries, vm => vm.$destroy())
|
|
95
104
|
}
|
|
96
105
|
|
|
97
|
-
#decorateVmMetadata(backup) {
|
|
106
|
+
#decorateVmMetadata(backup, timestamp) {
|
|
98
107
|
const { _warmMigration } = this._settings
|
|
99
108
|
const sr = this._sr
|
|
100
109
|
const vm = backup.vm
|
|
110
|
+
const job = this._job
|
|
111
|
+
const scheduleId = this._scheduleId
|
|
101
112
|
|
|
113
|
+
vm.name_label = `${vm.name_label} - ${job.name} - (${formatFilenameDate(timestamp)})`
|
|
114
|
+
// update other_config data as soon as possible to ensure the next job
|
|
115
|
+
// will be able to detect any partial transfer and lean them
|
|
102
116
|
vm.other_config[COPY_OF] = vm.uuid
|
|
117
|
+
vm.other_config[JOB_ID] = job.id
|
|
118
|
+
vm.other_config[SCHEDULE_ID] = scheduleId
|
|
119
|
+
vm.other_config[REPLICATED_TO_SR_UUID] = sr.uuid
|
|
120
|
+
// set the timestamp in the past to ensure any incomplete VM will be deleted on next run
|
|
121
|
+
vm.other_config[DATETIME] = formatFilenameDate(0)
|
|
122
|
+
|
|
123
|
+
vm.blocked_operations = {
|
|
124
|
+
start: 'Start operation for this vm is blocked, clone it if you want to use it.',
|
|
125
|
+
start_on: 'Start operation for this vm is blocked, clone it if you want to use it.',
|
|
126
|
+
}
|
|
127
|
+
|
|
103
128
|
if (!_warmMigration) {
|
|
104
129
|
vm.tags.push('Continuous Replication')
|
|
105
130
|
}
|
|
@@ -118,6 +143,11 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
|
|
|
118
143
|
|
|
119
144
|
Object.values(backup.vdis).forEach(vdi => {
|
|
120
145
|
vdi.other_config[COPY_OF] = vdi.uuid
|
|
146
|
+
vdi.other_config[JOB_ID] = job.id
|
|
147
|
+
vdi.other_config[SCHEDULE_ID] = scheduleId
|
|
148
|
+
vdi.other_config[REPLICATED_TO_SR_UUID] = sr.uuid
|
|
149
|
+
vdi.other_config[VM_UUID] = vm.uuid
|
|
150
|
+
|
|
121
151
|
if (sourceVdiUuids.length > 0) {
|
|
122
152
|
const baseReplicatedTo = replicatedVdis.filter(
|
|
123
153
|
replicatedVdi => replicatedVdi.other_config[COPY_OF] === vdi.other_config[BASE_DELTA_VDI]
|
|
@@ -144,12 +174,11 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
|
|
|
144
174
|
const sr = this._sr
|
|
145
175
|
const job = this._job
|
|
146
176
|
const scheduleId = this._scheduleId
|
|
147
|
-
|
|
148
177
|
const { uuid: srUuid, $xapi: xapi } = sr
|
|
149
|
-
|
|
178
|
+
|
|
150
179
|
let targetVmRef
|
|
151
180
|
await Task.run({ name: 'transfer' }, async () => {
|
|
152
|
-
targetVmRef = await importIncrementalVm(this.#decorateVmMetadata(deltaExport), sr)
|
|
181
|
+
targetVmRef = await importIncrementalVm(this.#decorateVmMetadata(deltaExport, timestamp), sr)
|
|
153
182
|
// size is mandatory to ensure the task have the right data
|
|
154
183
|
return {
|
|
155
184
|
size: Object.values(deltaExport.disks).reduce(
|
|
@@ -166,15 +195,8 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
|
|
|
166
195
|
!_warmMigration &&
|
|
167
196
|
targetVm.ha_restart_priority !== '' &&
|
|
168
197
|
Promise.all([targetVm.set_ha_restart_priority(''), targetVm.add_tags('HA disabled')]),
|
|
169
|
-
targetVm.set_name_label(`${vm.name_label} - ${job.name} - (${formatFilenameDate(timestamp)})`),
|
|
170
|
-
asyncMap(['start', 'start_on'], op =>
|
|
171
|
-
targetVm.update_blocked_operations(
|
|
172
|
-
op,
|
|
173
|
-
'Start operation for this vm is blocked, clone it if you want to use it.'
|
|
174
|
-
)
|
|
175
|
-
),
|
|
176
198
|
setVmOtherConfig(xapi, targetVmRef, {
|
|
177
|
-
timestamp,
|
|
199
|
+
timestamp, // updated at the end to mark the transfer as complete
|
|
178
200
|
jobId: job.id,
|
|
179
201
|
scheduleId,
|
|
180
202
|
vmUuid: vm.uuid,
|
|
@@ -20,9 +20,7 @@ export function listReplicatedVms(xapi, scheduleOrJobId, srUuid, vmUuid) {
|
|
|
20
20
|
'start' in object.blocked_operations &&
|
|
21
21
|
(oc[JOB_ID] === scheduleOrJobId || oc[SCHEDULE_ID] === scheduleOrJobId) &&
|
|
22
22
|
oc[REPLICATED_TO_SR_UUID] === srUuid &&
|
|
23
|
-
|
|
24
|
-
// 2018-03-28, JFT: to catch VMs replicated before this fix
|
|
25
|
-
oc[VM_UUID] === undefined)
|
|
23
|
+
oc[VM_UUID] === vmUuid
|
|
26
24
|
) {
|
|
27
25
|
vms[object.$id] = object
|
|
28
26
|
}
|
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.68.
|
|
11
|
+
"version": "0.68.1",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=14.18"
|
|
14
14
|
},
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@vates/disposable": "^0.1.6",
|
|
27
27
|
"@vates/fuse-vhd": "^2.1.2",
|
|
28
28
|
"@vates/generator-toolbox": "^1.1.0",
|
|
29
|
-
"@vates/nbd-client": "^3.2.
|
|
29
|
+
"@vates/nbd-client": "^3.2.3",
|
|
30
30
|
"@vates/parse-duration": "^0.1.1",
|
|
31
31
|
"@xen-orchestra/async-map": "^0.1.2",
|
|
32
32
|
"@xen-orchestra/disk-transform": "^1.2.1",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"tmp": "^0.2.1"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
|
-
"@xen-orchestra/xapi": "^8.6.
|
|
65
|
+
"@xen-orchestra/xapi": "^8.6.5"
|
|
66
66
|
},
|
|
67
67
|
"license": "AGPL-3.0-or-later",
|
|
68
68
|
"author": {
|