@xen-orchestra/backups 0.29.1 → 0.29.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 +6 -6
- package/_cleanVm.js +45 -19
- package/package.json +2 -2
package/RemoteAdapter.js
CHANGED
|
@@ -508,7 +508,7 @@ class RemoteAdapter {
|
|
|
508
508
|
return `${BACKUP_DIR}/${vmUuid}/cache.json.gz`
|
|
509
509
|
}
|
|
510
510
|
|
|
511
|
-
async
|
|
511
|
+
async _readCache(path) {
|
|
512
512
|
try {
|
|
513
513
|
return JSON.parse(await fromCallback(zlib.gunzip, await this.handler.readFile(path)))
|
|
514
514
|
} catch (error) {
|
|
@@ -521,15 +521,15 @@ class RemoteAdapter {
|
|
|
521
521
|
_updateCache = synchronized.withKey()(this._updateCache)
|
|
522
522
|
// eslint-disable-next-line no-dupe-class-members
|
|
523
523
|
async _updateCache(path, fn) {
|
|
524
|
-
const cache = await this
|
|
524
|
+
const cache = await this._readCache(path)
|
|
525
525
|
if (cache !== undefined) {
|
|
526
526
|
fn(cache)
|
|
527
527
|
|
|
528
|
-
await this
|
|
528
|
+
await this._writeCache(path, cache)
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
531
|
|
|
532
|
-
async
|
|
532
|
+
async _writeCache(path, data) {
|
|
533
533
|
try {
|
|
534
534
|
await this.handler.writeFile(path, await fromCallback(zlib.gzip, JSON.stringify(data)), { flags: 'w' })
|
|
535
535
|
} catch (error) {
|
|
@@ -577,7 +577,7 @@ class RemoteAdapter {
|
|
|
577
577
|
async _readCacheListVmBackups(vmUuid) {
|
|
578
578
|
const path = this.#getVmBackupsCache(vmUuid)
|
|
579
579
|
|
|
580
|
-
const cache = await this
|
|
580
|
+
const cache = await this._readCache(path)
|
|
581
581
|
if (cache !== undefined) {
|
|
582
582
|
debug('found VM backups cache, using it', { path })
|
|
583
583
|
return cache
|
|
@@ -590,7 +590,7 @@ class RemoteAdapter {
|
|
|
590
590
|
}
|
|
591
591
|
|
|
592
592
|
// detached async action, will not reject
|
|
593
|
-
this
|
|
593
|
+
this._writeCache(path, backups)
|
|
594
594
|
|
|
595
595
|
return backups
|
|
596
596
|
}
|
package/_cleanVm.js
CHANGED
|
@@ -311,7 +311,6 @@ exports.cleanVm = async function cleanVm(
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
const jsons = new Set()
|
|
314
|
-
let mustInvalidateCache = false
|
|
315
314
|
const xvas = new Set()
|
|
316
315
|
const xvaSums = []
|
|
317
316
|
const entries = await handler.list(vmDir, {
|
|
@@ -327,6 +326,20 @@ exports.cleanVm = async function cleanVm(
|
|
|
327
326
|
}
|
|
328
327
|
})
|
|
329
328
|
|
|
329
|
+
const cachePath = vmDir + '/cache.json.gz'
|
|
330
|
+
|
|
331
|
+
let mustRegenerateCache
|
|
332
|
+
{
|
|
333
|
+
const cache = await this._readCache(cachePath)
|
|
334
|
+
const actual = cache === undefined ? 0 : Object.keys(cache).length
|
|
335
|
+
const expected = jsons.size
|
|
336
|
+
|
|
337
|
+
mustRegenerateCache = actual !== expected
|
|
338
|
+
if (mustRegenerateCache) {
|
|
339
|
+
logWarn('unexpected number of entries in backup cache', { path: cachePath, actual, expected })
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
330
343
|
await asyncMap(xvas, async path => {
|
|
331
344
|
// check is not good enough to delete the file, the best we can do is report
|
|
332
345
|
// it
|
|
@@ -338,6 +351,8 @@ exports.cleanVm = async function cleanVm(
|
|
|
338
351
|
const unusedVhds = new Set(vhds)
|
|
339
352
|
const unusedXvas = new Set(xvas)
|
|
340
353
|
|
|
354
|
+
const backups = new Map()
|
|
355
|
+
|
|
341
356
|
// compile the list of unused XVAs and VHDs, and remove backup metadata which
|
|
342
357
|
// reference a missing XVA/VHD
|
|
343
358
|
await asyncMap(jsons, async json => {
|
|
@@ -350,19 +365,16 @@ exports.cleanVm = async function cleanVm(
|
|
|
350
365
|
return
|
|
351
366
|
}
|
|
352
367
|
|
|
368
|
+
let isBackupComplete
|
|
369
|
+
|
|
353
370
|
const { mode } = metadata
|
|
354
371
|
if (mode === 'full') {
|
|
355
372
|
const linkedXva = resolve('/', vmDir, metadata.xva)
|
|
356
|
-
|
|
373
|
+
isBackupComplete = xvas.has(linkedXva)
|
|
374
|
+
if (isBackupComplete) {
|
|
357
375
|
unusedXvas.delete(linkedXva)
|
|
358
376
|
} else {
|
|
359
377
|
logWarn('the XVA linked to the backup is missing', { backup: json, xva: linkedXva })
|
|
360
|
-
if (remove) {
|
|
361
|
-
logInfo('deleting incomplete backup', { path: json })
|
|
362
|
-
jsons.delete(json)
|
|
363
|
-
mustInvalidateCache = true
|
|
364
|
-
await handler.unlink(json)
|
|
365
|
-
}
|
|
366
378
|
}
|
|
367
379
|
} else if (mode === 'delta') {
|
|
368
380
|
const linkedVhds = (() => {
|
|
@@ -371,22 +383,28 @@ exports.cleanVm = async function cleanVm(
|
|
|
371
383
|
})()
|
|
372
384
|
|
|
373
385
|
const missingVhds = linkedVhds.filter(_ => !vhds.has(_))
|
|
386
|
+
isBackupComplete = missingVhds.length === 0
|
|
374
387
|
|
|
375
388
|
// FIXME: find better approach by keeping as much of the backup as
|
|
376
389
|
// possible (existing disks) even if one disk is missing
|
|
377
|
-
if (
|
|
390
|
+
if (isBackupComplete) {
|
|
378
391
|
linkedVhds.forEach(_ => unusedVhds.delete(_))
|
|
379
392
|
linkedVhds.forEach(path => {
|
|
380
393
|
vhdsToJSons[path] = json
|
|
381
394
|
})
|
|
382
395
|
} else {
|
|
383
396
|
logWarn('some VHDs linked to the backup are missing', { backup: json, missingVhds })
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (isBackupComplete) {
|
|
401
|
+
backups.set(json, metadata)
|
|
402
|
+
} else {
|
|
403
|
+
jsons.delete(json)
|
|
404
|
+
if (remove) {
|
|
405
|
+
logInfo('deleting incomplete backup', { backup: json })
|
|
406
|
+
mustRegenerateCache = true
|
|
407
|
+
await handler.unlink(json)
|
|
390
408
|
}
|
|
391
409
|
}
|
|
392
410
|
})
|
|
@@ -496,7 +514,7 @@ exports.cleanVm = async function cleanVm(
|
|
|
496
514
|
// check for the other that the size is the same as the real file size
|
|
497
515
|
|
|
498
516
|
await asyncMap(jsons, async metadataPath => {
|
|
499
|
-
const metadata =
|
|
517
|
+
const metadata = backups.get(metadataPath)
|
|
500
518
|
|
|
501
519
|
let fileSystemSize
|
|
502
520
|
const merged = metadataWithMergedVhd[metadataPath] !== undefined
|
|
@@ -538,6 +556,7 @@ exports.cleanVm = async function cleanVm(
|
|
|
538
556
|
// systematically update size after a merge
|
|
539
557
|
if ((merged || fixMetadata) && size !== fileSystemSize) {
|
|
540
558
|
metadata.size = fileSystemSize
|
|
559
|
+
mustRegenerateCache = true
|
|
541
560
|
try {
|
|
542
561
|
await handler.writeFile(metadataPath, JSON.stringify(metadata), { flags: 'w' })
|
|
543
562
|
} catch (error) {
|
|
@@ -546,9 +565,16 @@ exports.cleanVm = async function cleanVm(
|
|
|
546
565
|
}
|
|
547
566
|
})
|
|
548
567
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
568
|
+
if (mustRegenerateCache) {
|
|
569
|
+
const cache = {}
|
|
570
|
+
for (const [path, content] of backups.entries()) {
|
|
571
|
+
cache[path] = {
|
|
572
|
+
_filename: path,
|
|
573
|
+
id: path,
|
|
574
|
+
...content,
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
await this._writeCache(cachePath, cache)
|
|
552
578
|
}
|
|
553
579
|
|
|
554
580
|
return {
|
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.29.
|
|
11
|
+
"version": "0.29.2",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=14.6"
|
|
14
14
|
},
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"tmp": "^0.2.1"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"@xen-orchestra/xapi": "^1.5.
|
|
55
|
+
"@xen-orchestra/xapi": "^1.5.3"
|
|
56
56
|
},
|
|
57
57
|
"license": "AGPL-3.0-or-later",
|
|
58
58
|
"author": {
|