@xen-orchestra/backups 0.71.0 → 0.71.2
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/_incrementalVm.mjs
CHANGED
|
@@ -365,12 +365,16 @@ export const importIncrementalVm = defer(async function importIncrementalVm(
|
|
|
365
365
|
}
|
|
366
366
|
}),
|
|
367
367
|
])
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
368
|
+
|
|
369
|
+
// recreate vtpm (note there is normally only one VTPM per VM at most)
|
|
370
|
+
const existingVtpmRefs = await xapi.getField('VM', vmRef, 'VTPMs')
|
|
371
|
+
for (const vtpmRef of existingVtpmRefs ?? []) {
|
|
372
|
+
await xapi.call('VTPM_destroy', vtpmRef)
|
|
373
|
+
}
|
|
374
|
+
for (const contents of incrementalVm.vtpms ?? []) {
|
|
375
|
+
await xapi.VTPM_create({ VM: vmRef, contents })
|
|
376
|
+
}
|
|
377
|
+
|
|
374
378
|
const vm = await xapi.getRecord('VM', vmRef)
|
|
375
379
|
await Promise.all([
|
|
376
380
|
vmRecord.ha_always_run && xapi.setField('VM', vmRef, 'ha_always_run', true),
|
|
@@ -54,7 +54,7 @@ export const AbstractRemote = class AbstractRemoteVmBackupRunner extends Abstrac
|
|
|
54
54
|
config,
|
|
55
55
|
healthCheckSr,
|
|
56
56
|
job,
|
|
57
|
-
|
|
57
|
+
schedule,
|
|
58
58
|
vmUuid,
|
|
59
59
|
settings,
|
|
60
60
|
})
|
|
@@ -72,7 +72,7 @@ export const AbstractRemote = class AbstractRemoteVmBackupRunner extends Abstrac
|
|
|
72
72
|
config,
|
|
73
73
|
healthCheckSr,
|
|
74
74
|
job,
|
|
75
|
-
|
|
75
|
+
schedule,
|
|
76
76
|
vmUuid,
|
|
77
77
|
remoteId,
|
|
78
78
|
settings: targetSettings,
|
|
@@ -56,62 +56,77 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
|
|
|
56
56
|
})
|
|
57
57
|
debug('checkBaseVdis, got snapshot candidates,', snapshotCandidates.length)
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
diffDisk = new XapiDiskSource({ xapi: sr.$xapi, vdiRef: activeVdi.$ref, baseRef: snapshot.$ref })
|
|
84
|
-
await diffDisk.init()
|
|
85
|
-
if (diffDisk.getBlockIndexes().length === 0) {
|
|
86
|
-
const sourceUuid = snapshot.other_config?.[COPY_OF]
|
|
87
|
-
if (sourceUuid) {
|
|
88
|
-
this.#baseVdisBySourceUuid.set(sourceUuid, activeVdi)
|
|
59
|
+
if (snapshotCandidates.length > 0) {
|
|
60
|
+
// New snapshot-based flow (6.3+): verify no data was written between
|
|
61
|
+
// the target snapshot and its active VDI.
|
|
62
|
+
let targetVmRef
|
|
63
|
+
let canChainToTargetVm = true
|
|
64
|
+
await asyncEach(
|
|
65
|
+
snapshotCandidates,
|
|
66
|
+
async snapshot => {
|
|
67
|
+
let diffDisk
|
|
68
|
+
try {
|
|
69
|
+
const activeVdi = sr.$xapi.getObject(snapshot.$snapshot_of)
|
|
70
|
+
const userVbds = activeVdi.$VBDs?.filter(vbd => vbd.$VM && !vbd.$VM.is_control_domain) ?? []
|
|
71
|
+
if (userVbds.length !== 1) {
|
|
72
|
+
debug('checkBaseVdis, share vbd ', { ref: snapshot.$ref, userVbds })
|
|
73
|
+
// shared vdi ignore
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
const vm = userVbds[0].$VM
|
|
77
|
+
if (!('start' in vm.blocked_operations)) {
|
|
78
|
+
debug('checkBaseVdis, vm not blocked', { vmRef: vm.$ref })
|
|
79
|
+
// vm start unlocked
|
|
80
|
+
// not really an issue since we have check the delta
|
|
81
|
+
// but it indicates the users played with the blocked operations
|
|
82
|
+
return
|
|
89
83
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
84
|
+
diffDisk = new XapiDiskSource({ xapi: sr.$xapi, vdiRef: activeVdi.$ref, baseRef: snapshot.$ref })
|
|
85
|
+
await diffDisk.init()
|
|
86
|
+
if (diffDisk.getBlockIndexes().length === 0) {
|
|
87
|
+
const sourceUuid = snapshot.other_config?.[COPY_OF]
|
|
88
|
+
if (sourceUuid) {
|
|
89
|
+
this.#baseVdisBySourceUuid.set(sourceUuid, activeVdi)
|
|
90
|
+
}
|
|
91
|
+
// Track the target VM (the replicated VM to update on the next transfer).
|
|
92
|
+
targetVmRef = vm.$ref
|
|
93
|
+
} else {
|
|
94
|
+
// not empty, we will create a new VM
|
|
95
|
+
canChainToTargetVm = false
|
|
96
|
+
debug('checkBaseVdis, data between snapshot and active disk', {
|
|
97
|
+
vdiRef: snapshot.$ref,
|
|
98
|
+
nbBlocks: diffDisk.getBlockIndexes().length,
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
debug('checkBaseVdis, skipping snapshot', { ref: snapshot.$ref, error })
|
|
103
|
+
return
|
|
104
|
+
} finally {
|
|
105
|
+
await diffDisk?.close().catch(error => debug('checkBaseVdis, error closing', error))
|
|
99
106
|
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
} finally {
|
|
104
|
-
await diffDisk?.close().catch(error => debug('checkBaseVdis, error closing', error))
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
concurrency: 4,
|
|
105
110
|
}
|
|
106
|
-
|
|
107
|
-
{
|
|
108
|
-
concurrency: 4,
|
|
109
|
-
}
|
|
110
|
-
)
|
|
111
|
+
)
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
if (canChainToTargetVm && targetVmRef !== undefined) {
|
|
114
|
+
debug('checkBaseVdis,got a valid vm target', targetVmRef)
|
|
115
|
+
this._targetVmRef = targetVmRef
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
// Legacy fallback (upgrade from pre-6.3): no target snapshots exist yet,
|
|
119
|
+
// look for active (non-snapshot) VDIs with matching COPY_OF, like the old code did.
|
|
120
|
+
debug('checkBaseVdis, no snapshot candidates, falling back to legacy active VDI lookup')
|
|
121
|
+
const legacyVdis = sr.$VDIs.filter(vdi => {
|
|
122
|
+
return vdi?.managed && !vdi?.is_a_snapshot && baseUuidToSrcVdi.has(vdi?.other_config[COPY_OF])
|
|
123
|
+
})
|
|
124
|
+
for (const vdi of legacyVdis) {
|
|
125
|
+
const sourceUuid = vdi.other_config[COPY_OF]
|
|
126
|
+
if (sourceUuid) {
|
|
127
|
+
this.#baseVdisBySourceUuid.set(sourceUuid, vdi)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
115
130
|
}
|
|
116
131
|
|
|
117
132
|
for (const uuid of baseUuidToSrcVdi.keys()) {
|
|
@@ -107,7 +107,7 @@ export class AbstractAggregatedRemoteWriter {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
async getEntriesPerAdapter(adapter) {
|
|
110
|
-
const scheduleId = this.#props.
|
|
110
|
+
const scheduleId = this.#props.schedule.id
|
|
111
111
|
const vmUuid = this.#props.vmUuid
|
|
112
112
|
return (await adapter.listVmBackups(vmUuid, _ => _.scheduleId === scheduleId)).map(entry => ({
|
|
113
113
|
...entry,
|