@xen-orchestra/backups 0.40.0 → 0.42.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/RestoreMetadataBackup.mjs +6 -1
- package/_incrementalVm.mjs +2 -0
- package/_runners/_XoMetadataBackup.mjs +8 -2
- package/_runners/_vmRunners/IncrementalXapi.mjs +1 -0
- package/_runners/_writers/_MixinXapiWriter.mjs +1 -1
- package/merge-worker/cli.mjs +42 -29
- package/merge-worker/config.toml +1 -0
- package/package.json +8 -6
|
@@ -21,7 +21,12 @@ export class RestoreMetadataBackup {
|
|
|
21
21
|
})
|
|
22
22
|
} else {
|
|
23
23
|
const metadata = JSON.parse(await handler.readFile(join(backupId, 'metadata.json')))
|
|
24
|
-
|
|
24
|
+
const dataFileName = resolve(backupId, metadata.data ?? 'data.json')
|
|
25
|
+
const data = await handler.readFile(dataFileName)
|
|
26
|
+
|
|
27
|
+
// if data is JSON, sent it as a plain string, otherwise, consider the data as binary and encode it
|
|
28
|
+
const isJson = dataFileName.endsWith('.json')
|
|
29
|
+
return isJson ? data.toString() : { encoding: 'base64', data: data.toString('base64') }
|
|
25
30
|
}
|
|
26
31
|
}
|
|
27
32
|
}
|
package/_incrementalVm.mjs
CHANGED
|
@@ -41,6 +41,7 @@ export async function exportIncrementalVm(
|
|
|
41
41
|
fullVdisRequired = new Set(),
|
|
42
42
|
|
|
43
43
|
disableBaseTags = false,
|
|
44
|
+
preferNbd,
|
|
44
45
|
} = {}
|
|
45
46
|
) {
|
|
46
47
|
// refs of VM's VDIs → base's VDIs.
|
|
@@ -88,6 +89,7 @@ export async function exportIncrementalVm(
|
|
|
88
89
|
baseRef: baseVdi?.$ref,
|
|
89
90
|
cancelToken,
|
|
90
91
|
format: 'vhd',
|
|
92
|
+
preferNbd,
|
|
91
93
|
})
|
|
92
94
|
})
|
|
93
95
|
|
|
@@ -22,7 +22,13 @@ export class XoMetadataBackup {
|
|
|
22
22
|
const dir = `${scheduleDir}/${formatFilenameDate(timestamp)}`
|
|
23
23
|
|
|
24
24
|
const data = job.xoMetadata
|
|
25
|
-
|
|
25
|
+
let dataBaseName = './data'
|
|
26
|
+
|
|
27
|
+
// JSON data is sent as plain string, binary data is sent as an object with `data` and `encoding properties
|
|
28
|
+
const isJson = typeof data === 'string'
|
|
29
|
+
if (isJson) {
|
|
30
|
+
dataBaseName += '.json'
|
|
31
|
+
}
|
|
26
32
|
|
|
27
33
|
const metadata = JSON.stringify(
|
|
28
34
|
{
|
|
@@ -54,7 +60,7 @@ export class XoMetadataBackup {
|
|
|
54
60
|
async () => {
|
|
55
61
|
const handler = adapter.handler
|
|
56
62
|
const dirMode = this._config.dirMode
|
|
57
|
-
await handler.outputFile(dataFileName, data, { dirMode })
|
|
63
|
+
await handler.outputFile(dataFileName, isJson ? data : Buffer.from(data.data, data.encoding), { dirMode })
|
|
58
64
|
await handler.outputFile(metaDataFileName, metadata, {
|
|
59
65
|
dirMode,
|
|
60
66
|
})
|
|
@@ -41,6 +41,7 @@ export const IncrementalXapi = class IncrementalXapiVmBackupRunner extends Abstr
|
|
|
41
41
|
|
|
42
42
|
const deltaExport = await exportIncrementalVm(exportedVm, baseVm, {
|
|
43
43
|
fullVdisRequired,
|
|
44
|
+
preferNbd: this._settings.preferNbd,
|
|
44
45
|
})
|
|
45
46
|
// since NBD is network based, if one disk use nbd , all the disk use them
|
|
46
47
|
// except the suspended VDI
|
|
@@ -18,7 +18,7 @@ export const MixinXapiWriter = (BaseClass = Object) =>
|
|
|
18
18
|
const vdiRefs = await xapi.VM_getDisks(baseVm.$ref)
|
|
19
19
|
for (const vdiRef of vdiRefs) {
|
|
20
20
|
const vdi = xapi.getObject(vdiRef)
|
|
21
|
-
if (vdi.$SR.uuid !== this.
|
|
21
|
+
if (vdi.$SR.uuid !== this._healthCheckSr.uuid) {
|
|
22
22
|
return false
|
|
23
23
|
}
|
|
24
24
|
}
|
package/merge-worker/cli.mjs
CHANGED
|
@@ -2,18 +2,21 @@
|
|
|
2
2
|
// eslint-disable-next-line eslint-comments/disable-enable-pair
|
|
3
3
|
/* eslint-disable n/shebang */
|
|
4
4
|
|
|
5
|
+
import { asyncEach } from '@vates/async-each'
|
|
5
6
|
import { catchGlobalErrors } from '@xen-orchestra/log/configure'
|
|
6
7
|
import { createLogger } from '@xen-orchestra/log'
|
|
7
8
|
import { getSyncedHandler } from '@xen-orchestra/fs'
|
|
8
9
|
import { join } from 'node:path'
|
|
10
|
+
import { load as loadConfig } from 'app-conf'
|
|
9
11
|
import Disposable from 'promise-toolbox/Disposable'
|
|
10
|
-
import min from 'lodash/min.js'
|
|
11
12
|
|
|
12
13
|
import { getVmBackupDir } from '../_getVmBackupDir.mjs'
|
|
13
14
|
import { RemoteAdapter } from '../RemoteAdapter.mjs'
|
|
14
15
|
|
|
15
16
|
import { CLEAN_VM_QUEUE } from './index.mjs'
|
|
16
17
|
|
|
18
|
+
const APP_NAME = 'xo-merge-worker'
|
|
19
|
+
const APP_DIR = new URL('.', import.meta.url).pathname
|
|
17
20
|
// -------------------------------------------------------------------
|
|
18
21
|
|
|
19
22
|
catchGlobalErrors(createLogger('xo:backups:mergeWorker'))
|
|
@@ -34,6 +37,7 @@ const main = Disposable.wrap(async function* main(args) {
|
|
|
34
37
|
for (let i = 0; i < 10; ++i) {
|
|
35
38
|
const entries = await handler.list(CLEAN_VM_QUEUE)
|
|
36
39
|
if (entries.length !== 0) {
|
|
40
|
+
entries.sort()
|
|
37
41
|
return entries
|
|
38
42
|
}
|
|
39
43
|
await new Promise(timeoutResolver)
|
|
@@ -42,38 +46,47 @@ const main = Disposable.wrap(async function* main(args) {
|
|
|
42
46
|
|
|
43
47
|
let taskFiles
|
|
44
48
|
while ((taskFiles = await listRetry()) !== undefined) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
const { concurrency } = await loadConfig(APP_NAME, {
|
|
50
|
+
appDir: APP_DIR,
|
|
51
|
+
ignoreUnknownFormats: true,
|
|
52
|
+
})
|
|
53
|
+
await asyncEach(
|
|
54
|
+
taskFiles,
|
|
55
|
+
async taskFileBasename => {
|
|
56
|
+
const previousTaskFile = join(CLEAN_VM_QUEUE, taskFileBasename)
|
|
57
|
+
const taskFile = join(CLEAN_VM_QUEUE, '_' + taskFileBasename)
|
|
58
|
+
|
|
59
|
+
// move this task to the end
|
|
60
|
+
try {
|
|
61
|
+
await handler.rename(previousTaskFile, taskFile)
|
|
62
|
+
} catch (error) {
|
|
63
|
+
// this error occurs if the task failed too many times (i.e. too many `_` prefixes)
|
|
64
|
+
// there is nothing more that can be done
|
|
65
|
+
if (error.code === 'ENAMETOOLONG') {
|
|
66
|
+
await handler.unlink(previousTaskFile)
|
|
67
|
+
}
|
|
61
68
|
|
|
62
|
-
try {
|
|
63
|
-
const vmDir = getVmBackupDir(String(await handler.readFile(taskFile)))
|
|
64
|
-
try {
|
|
65
|
-
await adapter.cleanVm(vmDir, { merge: true, logInfo: info, logWarn: warn, remove: true })
|
|
66
|
-
} catch (error) {
|
|
67
|
-
// consider the clean successful if the VM dir is missing
|
|
68
|
-
if (error.code !== 'ENOENT') {
|
|
69
69
|
throw error
|
|
70
70
|
}
|
|
71
|
-
}
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
try {
|
|
73
|
+
const vmDir = getVmBackupDir(String(await handler.readFile(taskFile)))
|
|
74
|
+
try {
|
|
75
|
+
await adapter.cleanVm(vmDir, { merge: true, logInfo: info, logWarn: warn, remove: true })
|
|
76
|
+
} catch (error) {
|
|
77
|
+
// consider the clean successful if the VM dir is missing
|
|
78
|
+
if (error.code !== 'ENOENT') {
|
|
79
|
+
throw error
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
handler.unlink(taskFile).catch(error => warn('deleting task failure', { error }))
|
|
84
|
+
} catch (error) {
|
|
85
|
+
warn('failure handling task', { error })
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{ concurrency }
|
|
89
|
+
)
|
|
77
90
|
}
|
|
78
91
|
})
|
|
79
92
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
concurrency = 1
|
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.42.0",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=14.18"
|
|
14
14
|
},
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"test-integration": "node--test *.integ.mjs"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@iarna/toml": "^2.2.5",
|
|
20
21
|
"@kldzj/stream-throttle": "^1.1.1",
|
|
21
22
|
"@vates/async-each": "^1.0.0",
|
|
22
23
|
"@vates/cached-dns.lookup": "^1.0.0",
|
|
@@ -27,11 +28,12 @@
|
|
|
27
28
|
"@vates/nbd-client": "^2.0.0",
|
|
28
29
|
"@vates/parse-duration": "^0.1.1",
|
|
29
30
|
"@xen-orchestra/async-map": "^0.1.2",
|
|
30
|
-
"@xen-orchestra/fs": "^4.0
|
|
31
|
+
"@xen-orchestra/fs": "^4.1.0",
|
|
31
32
|
"@xen-orchestra/log": "^0.6.0",
|
|
32
33
|
"@xen-orchestra/template": "^0.1.0",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
34
|
+
"app-conf": "^2.3.0",
|
|
35
|
+
"compare-versions": "^6.0.0",
|
|
36
|
+
"d3-time-format": "^4.1.0",
|
|
35
37
|
"decorator-synchronized": "^0.6.0",
|
|
36
38
|
"golike-defer": "^0.5.1",
|
|
37
39
|
"limit-concurrency-decorator": "^0.5.0",
|
|
@@ -43,7 +45,7 @@
|
|
|
43
45
|
"tar": "^6.1.15",
|
|
44
46
|
"uuid": "^9.0.0",
|
|
45
47
|
"vhd-lib": "^4.5.0",
|
|
46
|
-
"xen-api": "^1.3.
|
|
48
|
+
"xen-api": "^1.3.5",
|
|
47
49
|
"yazl": "^2.5.1"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
@@ -54,7 +56,7 @@
|
|
|
54
56
|
"tmp": "^0.2.1"
|
|
55
57
|
},
|
|
56
58
|
"peerDependencies": {
|
|
57
|
-
"@xen-orchestra/xapi": "^3.
|
|
59
|
+
"@xen-orchestra/xapi": "^3.1.0"
|
|
58
60
|
},
|
|
59
61
|
"license": "AGPL-3.0-or-later",
|
|
60
62
|
"author": {
|