@xen-orchestra/backups 0.26.0 → 0.27.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/RemoteAdapter.js +1 -1
- package/_cleanVm.js +64 -32
- package/merge-worker/cli.js +1 -1
- package/package.json +4 -4
package/RemoteAdapter.js
CHANGED
|
@@ -279,7 +279,7 @@ class RemoteAdapter {
|
|
|
279
279
|
const dirs = new Set(files.map(file => dirname(file)))
|
|
280
280
|
for (const dir of dirs) {
|
|
281
281
|
// don't merge in main process, unused VHDs will be merged in the next backup run
|
|
282
|
-
await this.cleanVm(dir, { remove: true,
|
|
282
|
+
await this.cleanVm(dir, { remove: true, logWarn: warn })
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
const dedupedVmUuid = new Set(metadatas.map(_ => _.vm.uuid))
|
package/_cleanVm.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const assert = require('assert')
|
|
4
4
|
const sum = require('lodash/sum')
|
|
5
|
+
const UUID = require('uuid')
|
|
5
6
|
const { asyncMap } = require('@xen-orchestra/async-map')
|
|
6
7
|
const { Constants, mergeVhd, openVhd, VhdAbstract, VhdFile } = require('vhd-lib')
|
|
7
8
|
const { isVhdAlias, resolveVhdAlias } = require('vhd-lib/aliases')
|
|
@@ -50,16 +51,21 @@ const computeVhdsSize = (handler, vhdPaths) =>
|
|
|
50
51
|
async function mergeVhdChain(chain, { handler, logInfo, remove, merge }) {
|
|
51
52
|
assert(chain.length >= 2)
|
|
52
53
|
const chainCopy = [...chain]
|
|
53
|
-
const parent = chainCopy.
|
|
54
|
+
const parent = chainCopy.shift()
|
|
54
55
|
const children = chainCopy
|
|
55
56
|
|
|
56
57
|
if (merge) {
|
|
57
|
-
logInfo(
|
|
58
|
+
logInfo('will merge children into parent', { children, parent })
|
|
58
59
|
|
|
59
60
|
let done, total
|
|
60
61
|
const handle = setInterval(() => {
|
|
61
62
|
if (done !== undefined) {
|
|
62
|
-
logInfo(
|
|
63
|
+
logInfo('merge in progress', {
|
|
64
|
+
done,
|
|
65
|
+
parent,
|
|
66
|
+
progress: Math.round((100 * done) / total),
|
|
67
|
+
total,
|
|
68
|
+
})
|
|
63
69
|
}
|
|
64
70
|
}, 10e3)
|
|
65
71
|
|
|
@@ -121,15 +127,15 @@ async function checkAliases(
|
|
|
121
127
|
{ handler, logInfo = noop, logWarn = console.warn, remove = false }
|
|
122
128
|
) {
|
|
123
129
|
const aliasFound = []
|
|
124
|
-
for (const
|
|
125
|
-
const target = await resolveVhdAlias(handler,
|
|
130
|
+
for (const alias of aliasPaths) {
|
|
131
|
+
const target = await resolveVhdAlias(handler, alias)
|
|
126
132
|
|
|
127
133
|
if (!isVhdFile(target)) {
|
|
128
|
-
logWarn('alias references non VHD target', {
|
|
134
|
+
logWarn('alias references non VHD target', { alias, target })
|
|
129
135
|
if (remove) {
|
|
130
|
-
logInfo('removing alias and non VHD target', {
|
|
136
|
+
logInfo('removing alias and non VHD target', { alias, target })
|
|
131
137
|
await handler.unlink(target)
|
|
132
|
-
await handler.unlink(
|
|
138
|
+
await handler.unlink(alias)
|
|
133
139
|
}
|
|
134
140
|
continue
|
|
135
141
|
}
|
|
@@ -142,13 +148,13 @@ async function checkAliases(
|
|
|
142
148
|
// error during dispose should not trigger a deletion
|
|
143
149
|
}
|
|
144
150
|
} catch (error) {
|
|
145
|
-
logWarn('missing or broken alias target', {
|
|
151
|
+
logWarn('missing or broken alias target', { alias, target, error })
|
|
146
152
|
if (remove) {
|
|
147
153
|
try {
|
|
148
|
-
await VhdAbstract.unlink(handler,
|
|
154
|
+
await VhdAbstract.unlink(handler, alias)
|
|
149
155
|
} catch (error) {
|
|
150
156
|
if (error.code !== 'ENOENT') {
|
|
151
|
-
logWarn('error deleting alias target', {
|
|
157
|
+
logWarn('error deleting alias target', { alias, target, error })
|
|
152
158
|
}
|
|
153
159
|
}
|
|
154
160
|
}
|
|
@@ -158,17 +164,17 @@ async function checkAliases(
|
|
|
158
164
|
aliasFound.push(resolve('/', target))
|
|
159
165
|
}
|
|
160
166
|
|
|
161
|
-
const
|
|
167
|
+
const vhds = await handler.list(targetDataRepository, {
|
|
162
168
|
ignoreMissing: true,
|
|
163
169
|
prependDir: true,
|
|
164
170
|
})
|
|
165
171
|
|
|
166
|
-
|
|
167
|
-
if (!aliasFound.includes(
|
|
168
|
-
logWarn('no alias references VHD', {
|
|
172
|
+
await asyncMap(vhds, async path => {
|
|
173
|
+
if (!aliasFound.includes(path)) {
|
|
174
|
+
logWarn('no alias references VHD', { path })
|
|
169
175
|
if (remove) {
|
|
170
|
-
logInfo('deleting
|
|
171
|
-
await VhdAbstract.unlink(handler,
|
|
176
|
+
logInfo('deleting unused VHD', { path })
|
|
177
|
+
await VhdAbstract.unlink(handler, path)
|
|
172
178
|
}
|
|
173
179
|
}
|
|
174
180
|
})
|
|
@@ -187,6 +193,7 @@ exports.cleanVm = async function cleanVm(
|
|
|
187
193
|
const handler = this._handler
|
|
188
194
|
|
|
189
195
|
const vhdsToJSons = new Set()
|
|
196
|
+
const vhdById = new Map()
|
|
190
197
|
const vhdParents = { __proto__: null }
|
|
191
198
|
const vhdChildren = { __proto__: null }
|
|
192
199
|
|
|
@@ -208,12 +215,33 @@ exports.cleanVm = async function cleanVm(
|
|
|
208
215
|
}
|
|
209
216
|
vhdChildren[parent] = path
|
|
210
217
|
}
|
|
218
|
+
// Detect VHDs with the same UUIDs
|
|
219
|
+
//
|
|
220
|
+
// Due to a bug introduced in a1bcd35e2
|
|
221
|
+
const duplicate = vhdById.get(UUID.stringify(vhd.footer.uuid))
|
|
222
|
+
let vhdKept = vhd
|
|
223
|
+
if (duplicate !== undefined) {
|
|
224
|
+
logWarn('uuid is duplicated', { uuid: UUID.stringify(vhd.footer.uuid) })
|
|
225
|
+
if (duplicate.containsAllDataOf(vhd)) {
|
|
226
|
+
logWarn(`should delete ${path}`)
|
|
227
|
+
vhdKept = duplicate
|
|
228
|
+
vhds.delete(path)
|
|
229
|
+
} else if (vhd.containsAllDataOf(duplicate)) {
|
|
230
|
+
logWarn(`should delete ${duplicate._path}`)
|
|
231
|
+
vhds.delete(duplicate._path)
|
|
232
|
+
} else {
|
|
233
|
+
logWarn('same ids but different content')
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
logInfo('not duplicate', UUID.stringify(vhd.footer.uuid), path)
|
|
237
|
+
}
|
|
238
|
+
vhdById.set(UUID.stringify(vhdKept.footer.uuid), vhdKept)
|
|
211
239
|
})
|
|
212
240
|
} catch (error) {
|
|
213
241
|
vhds.delete(path)
|
|
214
242
|
logWarn('VHD check error', { path, error })
|
|
215
243
|
if (error?.code === 'ERR_ASSERTION' && remove) {
|
|
216
|
-
logInfo('deleting broken
|
|
244
|
+
logInfo('deleting broken VHD', { path })
|
|
217
245
|
return VhdAbstract.unlink(handler, path)
|
|
218
246
|
}
|
|
219
247
|
}
|
|
@@ -261,9 +289,9 @@ exports.cleanVm = async function cleanVm(
|
|
|
261
289
|
if (!vhds.has(parent)) {
|
|
262
290
|
vhds.delete(vhdPath)
|
|
263
291
|
|
|
264
|
-
logWarn('parent VHD is missing', { parent, vhdPath })
|
|
292
|
+
logWarn('parent VHD is missing', { parent, child: vhdPath })
|
|
265
293
|
if (remove) {
|
|
266
|
-
logInfo('deleting orphan VHD', { vhdPath })
|
|
294
|
+
logInfo('deleting orphan VHD', { path: vhdPath })
|
|
267
295
|
deletions.push(VhdAbstract.unlink(handler, vhdPath))
|
|
268
296
|
}
|
|
269
297
|
}
|
|
@@ -314,7 +342,7 @@ exports.cleanVm = async function cleanVm(
|
|
|
314
342
|
try {
|
|
315
343
|
metadata = JSON.parse(await handler.readFile(json))
|
|
316
344
|
} catch (error) {
|
|
317
|
-
logWarn('failed to read metadata
|
|
345
|
+
logWarn('failed to read backup metadata', { path: json, error })
|
|
318
346
|
jsons.delete(json)
|
|
319
347
|
return
|
|
320
348
|
}
|
|
@@ -325,9 +353,9 @@ exports.cleanVm = async function cleanVm(
|
|
|
325
353
|
if (xvas.has(linkedXva)) {
|
|
326
354
|
unusedXvas.delete(linkedXva)
|
|
327
355
|
} else {
|
|
328
|
-
logWarn('
|
|
356
|
+
logWarn('the XVA linked to the backup is missing', { backup: json, xva: linkedXva })
|
|
329
357
|
if (remove) {
|
|
330
|
-
logInfo('deleting incomplete backup', { json })
|
|
358
|
+
logInfo('deleting incomplete backup', { path: json })
|
|
331
359
|
jsons.delete(json)
|
|
332
360
|
await handler.unlink(json)
|
|
333
361
|
}
|
|
@@ -348,9 +376,9 @@ exports.cleanVm = async function cleanVm(
|
|
|
348
376
|
vhdsToJSons[path] = json
|
|
349
377
|
})
|
|
350
378
|
} else {
|
|
351
|
-
logWarn('some
|
|
379
|
+
logWarn('some VHDs linked to the backup are missing', { backup: json, missingVhds })
|
|
352
380
|
if (remove) {
|
|
353
|
-
logInfo('deleting incomplete backup', { json })
|
|
381
|
+
logInfo('deleting incomplete backup', { path: json })
|
|
354
382
|
jsons.delete(json)
|
|
355
383
|
await handler.unlink(json)
|
|
356
384
|
}
|
|
@@ -362,7 +390,7 @@ exports.cleanVm = async function cleanVm(
|
|
|
362
390
|
const unusedVhdsDeletion = []
|
|
363
391
|
const toMerge = []
|
|
364
392
|
{
|
|
365
|
-
// VHD chains (as list from
|
|
393
|
+
// VHD chains (as list from oldest to most recent) to merge indexed by most recent
|
|
366
394
|
// ancestor
|
|
367
395
|
const vhdChainsToMerge = { __proto__: null }
|
|
368
396
|
|
|
@@ -386,14 +414,14 @@ exports.cleanVm = async function cleanVm(
|
|
|
386
414
|
if (child !== undefined) {
|
|
387
415
|
const chain = getUsedChildChainOrDelete(child)
|
|
388
416
|
if (chain !== undefined) {
|
|
389
|
-
chain.
|
|
417
|
+
chain.unshift(vhd)
|
|
390
418
|
return chain
|
|
391
419
|
}
|
|
392
420
|
}
|
|
393
421
|
|
|
394
|
-
logWarn('unused VHD', { vhd })
|
|
422
|
+
logWarn('unused VHD', { path: vhd })
|
|
395
423
|
if (remove) {
|
|
396
|
-
logInfo('deleting unused VHD', { vhd })
|
|
424
|
+
logInfo('deleting unused VHD', { path: vhd })
|
|
397
425
|
unusedVhdsDeletion.push(VhdAbstract.unlink(handler, vhd))
|
|
398
426
|
}
|
|
399
427
|
}
|
|
@@ -474,11 +502,15 @@ exports.cleanVm = async function cleanVm(
|
|
|
474
502
|
|
|
475
503
|
// don't warn if the size has changed after a merge
|
|
476
504
|
if (!merged && fileSystemSize !== size) {
|
|
477
|
-
logWarn('incorrect size in metadata', {
|
|
505
|
+
logWarn('incorrect backup size in metadata', {
|
|
506
|
+
path: metadataPath,
|
|
507
|
+
actual: size ?? 'none',
|
|
508
|
+
expected: fileSystemSize,
|
|
509
|
+
})
|
|
478
510
|
}
|
|
479
511
|
}
|
|
480
512
|
} catch (error) {
|
|
481
|
-
logWarn('failed to get
|
|
513
|
+
logWarn('failed to get backup size', { backup: metadataPath, error })
|
|
482
514
|
return
|
|
483
515
|
}
|
|
484
516
|
|
|
@@ -488,7 +520,7 @@ exports.cleanVm = async function cleanVm(
|
|
|
488
520
|
try {
|
|
489
521
|
await handler.writeFile(metadataPath, JSON.stringify(metadata), { flags: 'w' })
|
|
490
522
|
} catch (error) {
|
|
491
|
-
logWarn('
|
|
523
|
+
logWarn('failed to update backup size in metadata', { path: metadataPath, error })
|
|
492
524
|
}
|
|
493
525
|
}
|
|
494
526
|
})
|
package/merge-worker/cli.js
CHANGED
|
@@ -64,7 +64,7 @@ const main = Disposable.wrap(async function* main(args) {
|
|
|
64
64
|
try {
|
|
65
65
|
const vmDir = getVmBackupDir(String(await handler.readFile(taskFile)))
|
|
66
66
|
try {
|
|
67
|
-
await adapter.cleanVm(vmDir, { merge: true,
|
|
67
|
+
await adapter.cleanVm(vmDir, { merge: true, logInfo: info, logWarn: warn, remove: true })
|
|
68
68
|
} catch (error) {
|
|
69
69
|
// consider the clean successful if the VM dir is missing
|
|
70
70
|
if (error.code !== 'ENOENT') {
|
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.27.2",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=14.6"
|
|
14
14
|
},
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@vates/disposable": "^0.1.1",
|
|
23
23
|
"@vates/parse-duration": "^0.1.1",
|
|
24
24
|
"@xen-orchestra/async-map": "^0.1.2",
|
|
25
|
-
"@xen-orchestra/fs": "^
|
|
25
|
+
"@xen-orchestra/fs": "^2.0.0",
|
|
26
26
|
"@xen-orchestra/log": "^0.3.0",
|
|
27
27
|
"@xen-orchestra/template": "^0.1.0",
|
|
28
28
|
"compare-versions": "^4.0.1",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"promise-toolbox": "^0.21.0",
|
|
39
39
|
"proper-lockfile": "^4.1.2",
|
|
40
40
|
"uuid": "^8.3.2",
|
|
41
|
-
"vhd-lib": "^3.
|
|
41
|
+
"vhd-lib": "^3.3.3",
|
|
42
42
|
"yazl": "^2.5.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"tmp": "^0.2.1"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@xen-orchestra/xapi": "^1.
|
|
49
|
+
"@xen-orchestra/xapi": "^1.4.1"
|
|
50
50
|
},
|
|
51
51
|
"license": "AGPL-3.0-or-later",
|
|
52
52
|
"author": {
|