@xen-orchestra/backups 0.71.3 → 0.72.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/HealthCheckVmBackup.mjs +2 -2
- package/ImportVmBackup.mjs +2 -2
- package/_backupWorker.mjs +3 -3
- package/_cleanVm.mjs +8 -3
- package/_incrementalVm.mjs +1 -1
- package/_runners/Metadata.mjs +18 -13
- package/_runners/VmsRemote.mjs +21 -17
- package/_runners/VmsXapi.mjs +24 -23
- package/_runners/_Abstract.mjs +10 -5
- package/_runners/_PoolMetadataBackup.mjs +3 -3
- package/_runners/_XoMetadataBackup.mjs +3 -3
- package/_runners/_vmRunners/IncrementalXapi.mjs +1 -1
- package/_runners/_vmRunners/_Abstract.mjs +2 -2
- package/_runners/_vmRunners/_AbstractRemote.mjs +1 -1
- package/_runners/_vmRunners/_AbstractXapi.mjs +2 -2
- package/_runners/_writers/FullRemoteWriter.mjs +6 -5
- package/_runners/_writers/FullXapiWriter.mjs +5 -5
- package/_runners/_writers/IncrementalRemoteWriter.mjs +10 -9
- package/_runners/_writers/IncrementalXapiWriter.mjs +14 -10
- package/_runners/_writers/_MixinRemoteWriter.mjs +3 -3
- package/_runners/_writers/_MixinXapiWriter.mjs +4 -4
- package/package.json +15 -14
- package/Task.mjs +0 -155
- package/_runners/_runTask.mjs +0 -5
package/HealthCheckVmBackup.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Task } from '
|
|
1
|
+
import { Task } from '@vates/task'
|
|
2
2
|
|
|
3
3
|
export class HealthCheckVmBackup {
|
|
4
4
|
#restoredVm
|
|
@@ -14,7 +14,7 @@ export class HealthCheckVmBackup {
|
|
|
14
14
|
async run() {
|
|
15
15
|
return Task.run(
|
|
16
16
|
{
|
|
17
|
-
name: 'vmstart',
|
|
17
|
+
properties: { name: 'vmstart' },
|
|
18
18
|
},
|
|
19
19
|
async () => {
|
|
20
20
|
let restoredVm = this.#restoredVm
|
package/ImportVmBackup.mjs
CHANGED
|
@@ -2,9 +2,9 @@ import assert from 'node:assert'
|
|
|
2
2
|
|
|
3
3
|
import { formatFilenameDate } from './_filenameDate.mjs'
|
|
4
4
|
import { importIncrementalVm } from './_incrementalVm.mjs'
|
|
5
|
-
import { Task } from './Task.mjs'
|
|
6
5
|
import { watchStreamSize } from './_watchStreamSize.mjs'
|
|
7
6
|
import { decorateClass } from '@vates/decorate-with'
|
|
7
|
+
import { Task } from '@vates/task'
|
|
8
8
|
import { createLogger } from '@xen-orchestra/log'
|
|
9
9
|
import { dirname, join } from 'node:path'
|
|
10
10
|
import pickBy from 'lodash/pickBy.js'
|
|
@@ -244,7 +244,7 @@ export class ImportVmBackup {
|
|
|
244
244
|
|
|
245
245
|
return Task.run(
|
|
246
246
|
{
|
|
247
|
-
name: 'transfer',
|
|
247
|
+
properties: { name: 'transfer' },
|
|
248
248
|
},
|
|
249
249
|
async () => {
|
|
250
250
|
const xapi = this._xapi
|
package/_backupWorker.mjs
CHANGED
|
@@ -14,10 +14,10 @@ import { decorateMethodsWith } from '@vates/decorate-with'
|
|
|
14
14
|
import { deduped } from '@vates/disposable/deduped.js'
|
|
15
15
|
import { getHandler } from '@xen-orchestra/fs'
|
|
16
16
|
import { parseDuration } from '@vates/parse-duration'
|
|
17
|
+
import { Task } from '@vates/task'
|
|
17
18
|
import { Xapi } from '@xen-orchestra/xapi'
|
|
18
19
|
|
|
19
20
|
import { RemoteAdapter } from './RemoteAdapter.mjs'
|
|
20
|
-
import { Task } from './Task.mjs'
|
|
21
21
|
|
|
22
22
|
createCachedLookup().patchGlobal()
|
|
23
23
|
|
|
@@ -178,8 +178,8 @@ process.on('message', async message => {
|
|
|
178
178
|
const result = message.runWithLogs
|
|
179
179
|
? await Task.run(
|
|
180
180
|
{
|
|
181
|
-
name: 'backup run',
|
|
182
|
-
|
|
181
|
+
properties: { name: 'backup run', ...message.data.jobData },
|
|
182
|
+
onProgress: data =>
|
|
183
183
|
emitMessage({
|
|
184
184
|
data,
|
|
185
185
|
type: 'log',
|
package/_cleanVm.mjs
CHANGED
|
@@ -9,8 +9,8 @@ import { RemoteVhdDisk } from './disks/RemoteVhdDisk.mjs'
|
|
|
9
9
|
import { RemoteVhdDiskChain } from './disks/RemoteVhdDiskChain.mjs'
|
|
10
10
|
import { MergeRemoteDisk } from './disks/MergeRemoteDisk.mjs'
|
|
11
11
|
|
|
12
|
-
import { Task } from './Task.mjs'
|
|
13
12
|
import { Disposable } from 'promise-toolbox'
|
|
13
|
+
import { Task } from '@vates/task'
|
|
14
14
|
import handlerPath from '@xen-orchestra/fs/path'
|
|
15
15
|
|
|
16
16
|
const { DISK_TYPES } = Constants
|
|
@@ -514,23 +514,27 @@ export async function cleanVm(
|
|
|
514
514
|
})
|
|
515
515
|
}
|
|
516
516
|
|
|
517
|
+
let totalMergedDataSize = 0
|
|
517
518
|
const metadataWithMergedVhd = {}
|
|
518
519
|
const doMerge = async () => {
|
|
519
520
|
await asyncMap(toMerge, async chain => {
|
|
520
|
-
const { finalDiskSize } = await limitedMergeVhdChain(handler, chain, {
|
|
521
|
+
const { finalDiskSize, mergedDataSize } = await limitedMergeVhdChain(handler, chain, {
|
|
521
522
|
logInfo,
|
|
522
523
|
logWarn,
|
|
523
524
|
remove,
|
|
524
525
|
mergeBlockConcurrency,
|
|
525
526
|
})
|
|
527
|
+
totalMergedDataSize += mergedDataSize
|
|
526
528
|
const metadataPath = vhdsToJSons[chain[chain.length - 1]] // all the chain should have the same metadata file
|
|
527
529
|
metadataWithMergedVhd[metadataPath] = (metadataWithMergedVhd[metadataPath] ?? 0) + finalDiskSize
|
|
528
530
|
})
|
|
531
|
+
|
|
532
|
+
return { size: totalMergedDataSize }
|
|
529
533
|
}
|
|
530
534
|
|
|
531
535
|
await Promise.all([
|
|
532
536
|
...unusedVhdsDeletion,
|
|
533
|
-
toMerge.length !== 0 && (merge ? Task.run({ name: 'merge' }, doMerge) : () => Promise.resolve()),
|
|
537
|
+
toMerge.length !== 0 && (merge ? Task.run({ properties: { name: 'merge' } }, doMerge) : () => Promise.resolve()),
|
|
534
538
|
asyncMap(unusedXvas, path => {
|
|
535
539
|
logWarn('unused XVA', { path })
|
|
536
540
|
if (remove) {
|
|
@@ -619,5 +623,6 @@ export async function cleanVm(
|
|
|
619
623
|
return {
|
|
620
624
|
// boolean whether some VHDs were merged (or should be merged)
|
|
621
625
|
merge: toMerge.length !== 0,
|
|
626
|
+
size: totalMergedDataSize,
|
|
622
627
|
}
|
|
623
628
|
}
|
package/_incrementalVm.mjs
CHANGED
|
@@ -4,9 +4,9 @@ import { asyncMap } from '@xen-orchestra/async-map'
|
|
|
4
4
|
import { CancelToken } from 'promise-toolbox'
|
|
5
5
|
import { compareVersions } from 'compare-versions'
|
|
6
6
|
import { defer } from 'golike-defer'
|
|
7
|
+
import { Task } from '@vates/task'
|
|
7
8
|
|
|
8
9
|
import { cancelableMap } from './_cancelableMap.mjs'
|
|
9
|
-
import { Task } from './Task.mjs'
|
|
10
10
|
import pick from 'lodash/pick.js'
|
|
11
11
|
import { BASE_DELTA_VDI, CONTENT_KEY, COPY_OF, VM_UUID } from './_otherConfig.mjs'
|
|
12
12
|
|
package/_runners/Metadata.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { asyncMap } from '@xen-orchestra/async-map'
|
|
2
|
+
import { Task } from '@vates/task'
|
|
2
3
|
import Disposable from 'promise-toolbox/Disposable'
|
|
3
4
|
import ignoreErrors from 'promise-toolbox/ignoreErrors'
|
|
4
5
|
|
|
@@ -6,9 +7,10 @@ import { extractIdsFromSimplePattern } from '../extractIdsFromSimplePattern.mjs'
|
|
|
6
7
|
import { PoolMetadataBackup } from './_PoolMetadataBackup.mjs'
|
|
7
8
|
import { XoMetadataBackup } from './_XoMetadataBackup.mjs'
|
|
8
9
|
import { DEFAULT_SETTINGS, Abstract } from './_Abstract.mjs'
|
|
9
|
-
import { runTask } from './_runTask.mjs'
|
|
10
10
|
import { getAdaptersByRemote } from './_getAdaptersByRemote.mjs'
|
|
11
11
|
|
|
12
|
+
const noop = Function.prototype
|
|
13
|
+
|
|
12
14
|
const DEFAULT_METADATA_SETTINGS = {
|
|
13
15
|
retentionPoolMetadata: 0,
|
|
14
16
|
retentionXoMetadata: 0,
|
|
@@ -55,13 +57,16 @@ export const Metadata = class MetadataBackupRunner extends Abstract {
|
|
|
55
57
|
poolIds.map(id =>
|
|
56
58
|
this._getRecord('pool', id).catch(error => {
|
|
57
59
|
// See https://github.com/vatesfr/xen-orchestra/commit/6aa6cfba8ec939c0288f0fa740f6dfad98c43cbb
|
|
58
|
-
|
|
60
|
+
Task.run(
|
|
59
61
|
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
properties: {
|
|
63
|
+
id,
|
|
64
|
+
name: 'get pool record',
|
|
65
|
+
type: 'pool',
|
|
66
|
+
},
|
|
62
67
|
},
|
|
63
68
|
() => Promise.reject(error)
|
|
64
|
-
)
|
|
69
|
+
).catch(noop)
|
|
65
70
|
})
|
|
66
71
|
)
|
|
67
72
|
),
|
|
@@ -81,11 +86,11 @@ export const Metadata = class MetadataBackupRunner extends Abstract {
|
|
|
81
86
|
if (pools.length !== 0 && settings.retentionPoolMetadata !== 0) {
|
|
82
87
|
promises.push(
|
|
83
88
|
asyncMap(pools, async pool =>
|
|
84
|
-
|
|
89
|
+
Task.run(
|
|
85
90
|
{
|
|
86
|
-
|
|
87
|
-
data: {
|
|
91
|
+
properties: {
|
|
88
92
|
id: pool.$id,
|
|
93
|
+
name: `Starting metadata backup for the pool (${pool.$id}). (${job.id})`,
|
|
89
94
|
pool,
|
|
90
95
|
poolMaster: await ignoreErrors.call(pool.$xapi.getRecord('host', pool.master)),
|
|
91
96
|
type: 'pool',
|
|
@@ -100,17 +105,17 @@ export const Metadata = class MetadataBackupRunner extends Abstract {
|
|
|
100
105
|
schedule,
|
|
101
106
|
settings,
|
|
102
107
|
}).run()
|
|
103
|
-
)
|
|
108
|
+
).catch(noop)
|
|
104
109
|
)
|
|
105
110
|
)
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
if (job.xoMetadata !== undefined && settings.retentionXoMetadata !== 0) {
|
|
109
114
|
promises.push(
|
|
110
|
-
|
|
115
|
+
Task.run(
|
|
111
116
|
{
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
properties: {
|
|
118
|
+
name: `Starting XO metadata backup. (${job.id})`,
|
|
114
119
|
type: 'xo',
|
|
115
120
|
},
|
|
116
121
|
},
|
|
@@ -122,7 +127,7 @@ export const Metadata = class MetadataBackupRunner extends Abstract {
|
|
|
122
127
|
schedule,
|
|
123
128
|
settings,
|
|
124
129
|
}).run()
|
|
125
|
-
)
|
|
130
|
+
).catch(noop)
|
|
126
131
|
)
|
|
127
132
|
}
|
|
128
133
|
await Promise.all(promises)
|
package/_runners/VmsRemote.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { asyncMapSettled } from '@xen-orchestra/async-map'
|
|
2
2
|
import Disposable from 'promise-toolbox/Disposable'
|
|
3
3
|
import { limitConcurrency } from 'limit-concurrency-decorator'
|
|
4
|
+
import { Task } from '@vates/task'
|
|
4
5
|
|
|
5
6
|
import { extractIdsFromSimplePattern } from '../extractIdsFromSimplePattern.mjs'
|
|
6
|
-
import { Task } from '../Task.mjs'
|
|
7
7
|
import { DEFAULT_SETTINGS, Abstract } from './_Abstract.mjs'
|
|
8
8
|
import { getAdaptersByRemote } from './_getAdaptersByRemote.mjs'
|
|
9
9
|
import { FullRemote } from './_vmRunners/FullRemote.mjs'
|
|
@@ -78,7 +78,7 @@ export const VmsRemote = class RemoteVmsBackupRunner extends Abstract {
|
|
|
78
78
|
}
|
|
79
79
|
nTriesByVmId[vmUuid]++
|
|
80
80
|
|
|
81
|
-
const taskStart = { name: 'backup VM',
|
|
81
|
+
const taskStart = { properties: { id: vmUuid, name: 'backup VM', type: 'VM' } }
|
|
82
82
|
const vmSettings = { ...settings, ...allSettings[vmUuid] }
|
|
83
83
|
const isLastRun = nTriesByVmId[vmUuid] === vmSettings.nRetriesVmBackupFailures + 1
|
|
84
84
|
|
|
@@ -113,23 +113,27 @@ export const VmsRemote = class RemoteVmsBackupRunner extends Abstract {
|
|
|
113
113
|
taskByVmId[vmUuid] = new Task(taskStart)
|
|
114
114
|
}
|
|
115
115
|
const task = taskByVmId[vmUuid]
|
|
116
|
+
// error has to be caught in the task to prevent its failure, but handled outside the task to execute another task.run()
|
|
117
|
+
let taskError
|
|
116
118
|
return task
|
|
117
|
-
.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
} else {
|
|
126
|
-
Task.warning(`Retry the VM mirror backup due to an error`, {
|
|
127
|
-
attempt: nTriesByVmId[vmUuid],
|
|
128
|
-
error: error.message,
|
|
129
|
-
})
|
|
130
|
-
queue.add(vmUuid)
|
|
131
|
-
}
|
|
119
|
+
.runInside(async () =>
|
|
120
|
+
vmBackup.run().catch(error => {
|
|
121
|
+
taskError = error
|
|
122
|
+
})
|
|
123
|
+
)
|
|
124
|
+
.then(result => {
|
|
125
|
+
if (taskError === undefined) {
|
|
126
|
+
return task.success(result)
|
|
132
127
|
}
|
|
128
|
+
if (isLastRun) {
|
|
129
|
+
return task.failure(taskError)
|
|
130
|
+
}
|
|
131
|
+
// don't end the task
|
|
132
|
+
task.warning(`Retry the VM mirror backup due to an error`, {
|
|
133
|
+
attempt: nTriesByVmId[vmUuid],
|
|
134
|
+
error: taskError.message,
|
|
135
|
+
})
|
|
136
|
+
queue.add(vmUuid)
|
|
133
137
|
})
|
|
134
138
|
.catch(noop)
|
|
135
139
|
}
|
package/_runners/VmsXapi.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { asyncMapSettled } from '@xen-orchestra/async-map'
|
|
2
2
|
import Disposable from 'promise-toolbox/Disposable'
|
|
3
3
|
import { limitConcurrency } from 'limit-concurrency-decorator'
|
|
4
|
+
import { Task } from '@vates/task'
|
|
4
5
|
|
|
5
6
|
import { extractIdsFromSimplePattern } from '../extractIdsFromSimplePattern.mjs'
|
|
6
|
-
import { Task } from '../Task.mjs'
|
|
7
7
|
import { DEFAULT_SETTINGS, Abstract } from './_Abstract.mjs'
|
|
8
|
-
import { runTask } from './_runTask.mjs'
|
|
9
8
|
import { getAdaptersByRemote } from './_getAdaptersByRemote.mjs'
|
|
10
9
|
import { IncrementalXapi } from './_vmRunners/IncrementalXapi.mjs'
|
|
11
10
|
import { FullXapi } from './_vmRunners/FullXapi.mjs'
|
|
@@ -65,13 +64,12 @@ export const VmsXapi = class VmsXapiBackupRunner extends Abstract {
|
|
|
65
64
|
Disposable.all(
|
|
66
65
|
extractIdsFromSimplePattern(job.srs).map(id =>
|
|
67
66
|
this._getRecord('SR', id).catch(error => {
|
|
68
|
-
|
|
67
|
+
Task.run(
|
|
69
68
|
{
|
|
70
|
-
name: 'get SR record',
|
|
71
|
-
data: { type: 'SR', id },
|
|
69
|
+
properties: { id, name: 'get SR record', type: 'SR' },
|
|
72
70
|
},
|
|
73
71
|
() => Promise.reject(error)
|
|
74
|
-
)
|
|
72
|
+
).catch(noop)
|
|
75
73
|
})
|
|
76
74
|
)
|
|
77
75
|
),
|
|
@@ -108,11 +106,12 @@ export const VmsXapi = class VmsXapiBackupRunner extends Abstract {
|
|
|
108
106
|
}
|
|
109
107
|
return taskByVmId[vmUuid]
|
|
110
108
|
}
|
|
111
|
-
const vmBackupFailed = error => {
|
|
109
|
+
const vmBackupFailed = async (error, task) => {
|
|
112
110
|
if (isLastRun) {
|
|
113
|
-
|
|
111
|
+
return task.failure(error)
|
|
114
112
|
} else {
|
|
115
|
-
|
|
113
|
+
// don't end the task
|
|
114
|
+
task.warning(`Retry the VM backup due to an error`, {
|
|
116
115
|
attempt: nTriesByVmId[vmUuid],
|
|
117
116
|
error: error.message,
|
|
118
117
|
})
|
|
@@ -126,19 +125,21 @@ export const VmsXapi = class VmsXapiBackupRunner extends Abstract {
|
|
|
126
125
|
nTriesByVmId[vmUuid]++
|
|
127
126
|
|
|
128
127
|
const vmSettings = { ...settings, ...allSettings[vmUuid] }
|
|
129
|
-
const taskStart = { name: 'backup VM',
|
|
128
|
+
const taskStart = { properties: { id: vmUuid, name: 'backup VM', type: 'VM' } }
|
|
130
129
|
const isLastRun = nTriesByVmId[vmUuid] === vmSettings.nRetriesVmBackupFailures + 1
|
|
131
130
|
|
|
132
131
|
return this._getRecord('VM', vmUuid).then(
|
|
133
132
|
disposableVm =>
|
|
134
133
|
Disposable.use(disposableVm, async vm => {
|
|
135
|
-
if (taskStart.
|
|
136
|
-
taskStart.
|
|
134
|
+
if (taskStart.properties.name_label === undefined) {
|
|
135
|
+
taskStart.properties.name_label = vm.name_label
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
const task = getVmTask()
|
|
139
|
+
// error has to be caught in the task to prevent its failure, but handled outside the task to execute another task.run()
|
|
140
|
+
let taskError
|
|
140
141
|
return task
|
|
141
|
-
.
|
|
142
|
+
.runInside(async () => {
|
|
142
143
|
const opts = {
|
|
143
144
|
baseSettings,
|
|
144
145
|
config,
|
|
@@ -164,21 +165,21 @@ export const VmsXapi = class VmsXapiBackupRunner extends Abstract {
|
|
|
164
165
|
throw new Error(`Job mode ${job.mode} not implemented`)
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
return vmBackup.run().catch(error => {
|
|
169
|
+
taskError = error
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
.then(result => {
|
|
173
|
+
if (taskError === undefined) {
|
|
170
174
|
task.success(result)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
vmBackupFailed(
|
|
175
|
+
} else {
|
|
176
|
+
// ending the task with error or not ending the task
|
|
177
|
+
vmBackupFailed(taskError, task)
|
|
174
178
|
}
|
|
175
179
|
})
|
|
176
180
|
.catch(noop) // errors are handled by logs
|
|
177
181
|
}),
|
|
178
|
-
error =>
|
|
179
|
-
getVmTask().run(() => {
|
|
180
|
-
vmBackupFailed(error)
|
|
181
|
-
})
|
|
182
|
+
error => vmBackupFailed(error, getVmTask())
|
|
182
183
|
)
|
|
183
184
|
}
|
|
184
185
|
const { concurrency } = settings
|
package/_runners/_Abstract.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import Disposable from 'promise-toolbox/Disposable'
|
|
2
2
|
import pTimeout from 'promise-toolbox/timeout'
|
|
3
3
|
import { compileTemplate } from '@xen-orchestra/template'
|
|
4
|
-
import { runTask } from './_runTask.mjs'
|
|
5
4
|
import { RemoteTimeoutError } from './_RemoteTimeoutError.mjs'
|
|
5
|
+
import { Task } from '@vates/task'
|
|
6
|
+
|
|
7
|
+
const noop = Function.prototype
|
|
6
8
|
|
|
7
9
|
export const DEFAULT_SETTINGS = {
|
|
8
10
|
getRemoteTimeout: 300e3,
|
|
@@ -36,13 +38,16 @@ export const Abstract = class AbstractRunner {
|
|
|
36
38
|
})
|
|
37
39
|
} catch (error) {
|
|
38
40
|
// See https://github.com/vatesfr/xen-orchestra/commit/6aa6cfba8ec939c0288f0fa740f6dfad98c43cbb
|
|
39
|
-
|
|
41
|
+
Task.run(
|
|
40
42
|
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
properties: {
|
|
44
|
+
id: remoteId,
|
|
45
|
+
name: 'get remote adapter',
|
|
46
|
+
type: 'remote',
|
|
47
|
+
},
|
|
43
48
|
},
|
|
44
49
|
() => Promise.reject(error)
|
|
45
|
-
)
|
|
50
|
+
).catch(noop)
|
|
46
51
|
}
|
|
47
52
|
}
|
|
48
53
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { asyncMap } from '@xen-orchestra/async-map'
|
|
2
|
+
import { Task } from '@vates/task'
|
|
2
3
|
|
|
3
4
|
import { DIR_XO_POOL_METADATA_BACKUPS } from '../RemoteAdapter.mjs'
|
|
4
5
|
import { forkStreamUnpipe } from './_forkStreamUnpipe.mjs'
|
|
5
6
|
import { formatFilenameDate } from '../_filenameDate.mjs'
|
|
6
|
-
import { Task } from '../Task.mjs'
|
|
7
7
|
|
|
8
8
|
export const PATH_DB_DUMP = '/pool/xmldbdump'
|
|
9
9
|
|
|
@@ -54,8 +54,8 @@ export class PoolMetadataBackup {
|
|
|
54
54
|
([remoteId, adapter]) =>
|
|
55
55
|
Task.run(
|
|
56
56
|
{
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
properties: {
|
|
58
|
+
name: `Starting metadata backup for the pool (${pool.$id}) for the remote (${remoteId}). (${job.id})`,
|
|
59
59
|
id: remoteId,
|
|
60
60
|
type: 'remote',
|
|
61
61
|
},
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { asyncMap } from '@xen-orchestra/async-map'
|
|
2
2
|
import { join } from '@xen-orchestra/fs/path'
|
|
3
|
+
import { Task } from '@vates/task'
|
|
3
4
|
|
|
4
5
|
import { DIR_XO_CONFIG_BACKUPS } from '../RemoteAdapter.mjs'
|
|
5
6
|
import { formatFilenameDate } from '../_filenameDate.mjs'
|
|
6
|
-
import { Task } from '../Task.mjs'
|
|
7
7
|
|
|
8
8
|
export class XoMetadataBackup {
|
|
9
9
|
constructor({ config, job, remoteAdapters, schedule, settings }) {
|
|
@@ -51,8 +51,8 @@ export class XoMetadataBackup {
|
|
|
51
51
|
([remoteId, adapter]) =>
|
|
52
52
|
Task.run(
|
|
53
53
|
{
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
properties: {
|
|
55
|
+
name: `Starting XO metadata backup for the remote (${remoteId}). (${job.id})`,
|
|
56
56
|
id: remoteId,
|
|
57
57
|
type: 'remote',
|
|
58
58
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createLogger } from '@xen-orchestra/log'
|
|
2
|
+
import { Task } from '@vates/task'
|
|
2
3
|
import keyBy from 'lodash/keyBy.js'
|
|
3
4
|
|
|
4
5
|
import { AbstractXapi } from './_AbstractXapi.mjs'
|
|
@@ -6,7 +7,6 @@ import { forkDeltaExport } from './_forkDeltaExport.mjs'
|
|
|
6
7
|
import { exportIncrementalVm } from '../../_incrementalVm.mjs'
|
|
7
8
|
import { IncrementalRemoteWriter } from '../_writers/IncrementalRemoteWriter.mjs'
|
|
8
9
|
import { IncrementalXapiWriter } from '../_writers/IncrementalXapiWriter.mjs'
|
|
9
|
-
import { Task } from '../../Task.mjs'
|
|
10
10
|
import {
|
|
11
11
|
DATETIME,
|
|
12
12
|
DELTA_CHAIN_LENGTH,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { asyncMap } from '@xen-orchestra/async-map'
|
|
2
2
|
import { createLogger } from '@xen-orchestra/log'
|
|
3
|
-
import { Task } from '
|
|
3
|
+
import { Task } from '@vates/task'
|
|
4
4
|
|
|
5
5
|
const { debug, warn } = createLogger('xo:backups:AbstractVmRunner')
|
|
6
6
|
|
|
@@ -83,7 +83,7 @@ export const Abstract = class AbstractVmBackupRunner {
|
|
|
83
83
|
// create a task to have an info in the logs and reports
|
|
84
84
|
return Task.run(
|
|
85
85
|
{
|
|
86
|
-
name: 'health check',
|
|
86
|
+
properties: { name: 'health check' },
|
|
87
87
|
},
|
|
88
88
|
() => {
|
|
89
89
|
Task.info(`This VM doesn't match the health check's tags for this schedule`)
|
|
@@ -4,12 +4,12 @@ import { decorateMethodsWith } from '@vates/decorate-with'
|
|
|
4
4
|
import { defer } from 'golike-defer'
|
|
5
5
|
import { Disposable } from 'promise-toolbox'
|
|
6
6
|
import { createPredicate } from 'value-matcher'
|
|
7
|
+
import { Task } from '@vates/task'
|
|
7
8
|
|
|
8
9
|
import { getVmBackupDir } from '../../_getVmBackupDir.mjs'
|
|
9
10
|
|
|
10
11
|
import { Abstract } from './_Abstract.mjs'
|
|
11
12
|
import { extractIdsFromSimplePattern } from '../../extractIdsFromSimplePattern.mjs'
|
|
12
|
-
import { Task } from '../../Task.mjs'
|
|
13
13
|
|
|
14
14
|
export const AbstractRemote = class AbstractRemoteVmBackupRunner extends Abstract {
|
|
15
15
|
_filterPredicate
|
|
@@ -6,9 +6,9 @@ import { asyncMap } from '@xen-orchestra/async-map'
|
|
|
6
6
|
import { asyncEach } from '@vates/async-each'
|
|
7
7
|
import { decorateMethodsWith } from '@vates/decorate-with'
|
|
8
8
|
import { defer } from 'golike-defer'
|
|
9
|
+
import { Task } from '@vates/task'
|
|
9
10
|
|
|
10
11
|
import { getOldEntries } from '../../_getOldEntries.mjs'
|
|
11
|
-
import { Task } from '../../Task.mjs'
|
|
12
12
|
import { Abstract } from './_Abstract.mjs'
|
|
13
13
|
import {
|
|
14
14
|
COPY_OF,
|
|
@@ -186,7 +186,7 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
|
|
|
186
186
|
const settings = this._settings
|
|
187
187
|
|
|
188
188
|
if (await this._mustDoSnapshot()) {
|
|
189
|
-
await Task.run({ name: 'snapshot' }, async () => {
|
|
189
|
+
await Task.run({ properties: { name: 'snapshot' } }, async () => {
|
|
190
190
|
if (!settings.bypassVdiChainsCheck) {
|
|
191
191
|
await vm.$assertHealthyVdiChains()
|
|
192
192
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { Task } from '@vates/task'
|
|
2
|
+
|
|
1
3
|
import { formatFilenameDate } from '../../_filenameDate.mjs'
|
|
2
4
|
import { getOldEntries } from '../../_getOldEntries.mjs'
|
|
3
|
-
import { Task } from '../../Task.mjs'
|
|
4
5
|
|
|
5
6
|
import { MixinRemoteWriter } from './_MixinRemoteWriter.mjs'
|
|
6
7
|
import { AbstractFullWriter } from './_AbstractFullWriter.mjs'
|
|
@@ -9,11 +10,11 @@ export class FullRemoteWriter extends MixinRemoteWriter(AbstractFullWriter) {
|
|
|
9
10
|
constructor(props) {
|
|
10
11
|
super(props)
|
|
11
12
|
|
|
12
|
-
this.run = Task.
|
|
13
|
+
this.run = Task.wrap(
|
|
13
14
|
{
|
|
14
|
-
|
|
15
|
-
data: {
|
|
15
|
+
properties: {
|
|
16
16
|
id: props.remoteId,
|
|
17
|
+
name: 'export',
|
|
17
18
|
type: 'remote',
|
|
18
19
|
|
|
19
20
|
// necessary?
|
|
@@ -64,7 +65,7 @@ export class FullRemoteWriter extends MixinRemoteWriter(AbstractFullWriter) {
|
|
|
64
65
|
await deleteOldBackups()
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
await Task.run({ name: 'transfer' }, async () => {
|
|
68
|
+
await Task.run({ properties: { name: 'transfer' } }, async () => {
|
|
68
69
|
await adapter.outputStream(dataFilename, stream, {
|
|
69
70
|
maxStreamLength,
|
|
70
71
|
streamLength,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import ignoreErrors from 'promise-toolbox/ignoreErrors'
|
|
2
2
|
import { asyncMap, asyncMapSettled } from '@xen-orchestra/async-map'
|
|
3
|
+
import { Task } from '@vates/task'
|
|
3
4
|
|
|
4
5
|
import { formatFilenameDate } from '../../_filenameDate.mjs'
|
|
5
6
|
import { getOldEntries } from '../../_getOldEntries.mjs'
|
|
6
|
-
import { Task } from '../../Task.mjs'
|
|
7
7
|
|
|
8
8
|
import { AbstractFullWriter } from './_AbstractFullWriter.mjs'
|
|
9
9
|
import { MixinXapiWriter } from './_MixinXapiWriter.mjs'
|
|
@@ -14,11 +14,11 @@ export class FullXapiWriter extends MixinXapiWriter(AbstractFullWriter) {
|
|
|
14
14
|
constructor(props) {
|
|
15
15
|
super(props)
|
|
16
16
|
|
|
17
|
-
this.run = Task.
|
|
17
|
+
this.run = Task.wrap(
|
|
18
18
|
{
|
|
19
|
-
|
|
20
|
-
data: {
|
|
19
|
+
properties: {
|
|
21
20
|
id: props.sr.uuid,
|
|
21
|
+
name: 'export',
|
|
22
22
|
name_label: this._sr.name_label,
|
|
23
23
|
type: 'SR',
|
|
24
24
|
|
|
@@ -52,7 +52,7 @@ export class FullXapiWriter extends MixinXapiWriter(AbstractFullWriter) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
let targetVmRef
|
|
55
|
-
await Task.run({ name: 'transfer' }, async () => {
|
|
55
|
+
await Task.run({ properties: { name: 'transfer' } }, async () => {
|
|
56
56
|
targetVmRef = await xapi.VM_import(stream, sr.$ref, vm =>
|
|
57
57
|
Promise.all([
|
|
58
58
|
!_warmMigration && vm.add_tags('Disaster Recovery'),
|
|
@@ -7,10 +7,10 @@ import { createLogger } from '@xen-orchestra/log'
|
|
|
7
7
|
import { decorateClass } from '@vates/decorate-with'
|
|
8
8
|
import { defer } from 'golike-defer'
|
|
9
9
|
import { dirname, basename } from 'node:path'
|
|
10
|
+
import { Task } from '@vates/task'
|
|
10
11
|
|
|
11
12
|
import { formatFilenameDate } from '../../_filenameDate.mjs'
|
|
12
13
|
import { getOldEntries } from '../../_getOldEntries.mjs'
|
|
13
|
-
import { Task } from '../../Task.mjs'
|
|
14
14
|
|
|
15
15
|
import { MixinRemoteWriter } from './_MixinRemoteWriter.mjs'
|
|
16
16
|
import { AbstractIncrementalWriter } from './_AbstractIncrementalWriter.mjs'
|
|
@@ -72,19 +72,20 @@ export class IncrementalRemoteWriter extends MixinRemoteWriter(AbstractIncrement
|
|
|
72
72
|
prepare({ isFull }) {
|
|
73
73
|
// create the task related to this export and ensure all methods are called in this context
|
|
74
74
|
const task = new Task({
|
|
75
|
-
|
|
76
|
-
data: {
|
|
75
|
+
properties: {
|
|
77
76
|
id: this._remoteId,
|
|
78
77
|
isFull,
|
|
78
|
+
name: 'export',
|
|
79
79
|
type: 'remote',
|
|
80
80
|
},
|
|
81
81
|
})
|
|
82
|
-
this.
|
|
83
|
-
this.
|
|
84
|
-
this.
|
|
85
|
-
this.
|
|
82
|
+
this._prepare = task.wrapInside(this._prepare)
|
|
83
|
+
this.transfer = task.wrapInside(this.transfer)
|
|
84
|
+
this.healthCheck = task.wrapInside(this.healthCheck)
|
|
85
|
+
this.cleanup = task.wrapInside(this.cleanup)
|
|
86
|
+
this.afterBackup = task.wrap(this.afterBackup)
|
|
86
87
|
|
|
87
|
-
return
|
|
88
|
+
return this._prepare()
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
async _prepare() {
|
|
@@ -223,7 +224,7 @@ export class IncrementalRemoteWriter extends MixinRemoteWriter(AbstractIncrement
|
|
|
223
224
|
vtpms: deltaExport.vtpms,
|
|
224
225
|
}
|
|
225
226
|
let size = 0
|
|
226
|
-
await Task.run({ name: 'transfer' }, async () => {
|
|
227
|
+
await Task.run({ properties: { name: 'transfer' } }, async () => {
|
|
227
228
|
await asyncEach(
|
|
228
229
|
Object.entries(deltaExport.disks),
|
|
229
230
|
async ([diskRef, disk]) => {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import humanFormat from 'human-format'
|
|
2
2
|
|
|
3
3
|
import { asyncMapSettled } from '@xen-orchestra/async-map'
|
|
4
|
+
import { Task } from '@vates/task'
|
|
4
5
|
import ignoreErrors from 'promise-toolbox/ignoreErrors'
|
|
5
6
|
|
|
6
7
|
import { getOldEntries } from '../../_getOldEntries.mjs'
|
|
7
8
|
import { importIncrementalVm } from '../../_incrementalVm.mjs'
|
|
8
|
-
import { Task } from '../../Task.mjs'
|
|
9
9
|
|
|
10
10
|
import { AbstractIncrementalWriter } from './_AbstractIncrementalWriter.mjs'
|
|
11
11
|
import { MixinXapiWriter } from './_MixinXapiWriter.mjs'
|
|
@@ -151,20 +151,24 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
|
|
|
151
151
|
prepare({ isFull }) {
|
|
152
152
|
// create the task related to this export and ensure all methods are called in this context
|
|
153
153
|
const task = new Task({
|
|
154
|
-
|
|
155
|
-
data: {
|
|
154
|
+
properties: {
|
|
156
155
|
id: this._sr.uuid,
|
|
157
156
|
isFull,
|
|
157
|
+
name: 'export',
|
|
158
158
|
name_label: this._sr.name_label,
|
|
159
159
|
type: 'SR',
|
|
160
160
|
},
|
|
161
161
|
})
|
|
162
|
-
|
|
163
|
-
this.transfer = task.
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
this._prepare = task.wrapInside(this._prepare)
|
|
163
|
+
this.transfer = task.wrapInside(this.transfer)
|
|
164
|
+
if (this._healthCheckSr !== undefined) {
|
|
165
|
+
this.cleanup = task.wrapInside(this.cleanup)
|
|
166
|
+
this.healthCheck = task.wrap(this.healthCheck)
|
|
167
|
+
} else {
|
|
168
|
+
this.cleanup = task.wrap(this.cleanup)
|
|
169
|
+
}
|
|
166
170
|
|
|
167
|
-
return
|
|
171
|
+
return this._prepare(isFull)
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
async _prepare(isFull) {
|
|
@@ -265,7 +269,7 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
|
|
|
265
269
|
const { uuid: srUuid, $xapi: xapi } = sr
|
|
266
270
|
|
|
267
271
|
let targetVmRef
|
|
268
|
-
await Task.run({ name: 'transfer' }, async () => {
|
|
272
|
+
await Task.run({ properties: { name: 'transfer' } }, async () => {
|
|
269
273
|
targetVmRef = await importIncrementalVm(this.#decorateVmMetadata(deltaExport, timestamp), sr, {
|
|
270
274
|
targetRef: this._targetVmRef,
|
|
271
275
|
})
|
|
@@ -289,7 +293,7 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
|
|
|
289
293
|
` -- last replication: ${formatFilenameDate(timestamp)} ${humanFormat.bytes(size)} read`
|
|
290
294
|
)
|
|
291
295
|
// take a snapshot to ensure these data are not modified until next snapshot
|
|
292
|
-
await Task.run({ name: 'target snapshot' }, async () => {
|
|
296
|
+
await Task.run({ properties: { name: 'target snapshot' } }, async () => {
|
|
293
297
|
await xapi.VM_snapshot(targetVmRef, {
|
|
294
298
|
name_label: `${vm.name_label} - ${job.name} / ${schedule.name} ${formatFilenameDate(timestamp)}`,
|
|
295
299
|
})
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createLogger } from '@xen-orchestra/log'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
|
+
import { Task } from '@vates/task'
|
|
3
4
|
import assert from 'node:assert'
|
|
4
5
|
|
|
5
6
|
import { formatFilenameDate } from '../../_filenameDate.mjs'
|
|
6
7
|
import { getVmBackupDir } from '../../_getVmBackupDir.mjs'
|
|
7
8
|
import { HealthCheckVmBackup } from '../../HealthCheckVmBackup.mjs'
|
|
8
9
|
import { ImportVmBackup } from '../../ImportVmBackup.mjs'
|
|
9
|
-
import { Task } from '../../Task.mjs'
|
|
10
10
|
import * as MergeWorker from '../../merge-worker/index.mjs'
|
|
11
11
|
import ms from 'ms'
|
|
12
12
|
import { getEntryStatus } from '../../_getOldEntries.mjs'
|
|
@@ -28,7 +28,7 @@ export const MixinRemoteWriter = (BaseClass = Object) =>
|
|
|
28
28
|
|
|
29
29
|
async _cleanVm(options) {
|
|
30
30
|
try {
|
|
31
|
-
return await Task.run({ name: 'clean-vm' }, () => {
|
|
31
|
+
return await Task.run({ properties: { name: 'clean-vm' } }, () => {
|
|
32
32
|
return this._adapter.cleanVm(this._vmBackupDir, {
|
|
33
33
|
...options,
|
|
34
34
|
fixMetadata: true,
|
|
@@ -100,7 +100,7 @@ export const MixinRemoteWriter = (BaseClass = Object) =>
|
|
|
100
100
|
}
|
|
101
101
|
return Task.run(
|
|
102
102
|
{
|
|
103
|
-
name: 'health check',
|
|
103
|
+
properties: { name: 'health check' },
|
|
104
104
|
},
|
|
105
105
|
async () => {
|
|
106
106
|
const xapi = sr.$xapi
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
|
+
import { Task } from '@vates/task'
|
|
2
3
|
|
|
3
4
|
import { HealthCheckVmBackup } from '../../HealthCheckVmBackup.mjs'
|
|
4
|
-
import { Task } from '../../Task.mjs'
|
|
5
5
|
import ms from 'ms'
|
|
6
6
|
|
|
7
7
|
export const MixinXapiWriter = (BaseClass = Object) =>
|
|
@@ -33,7 +33,7 @@ export const MixinXapiWriter = (BaseClass = Object) =>
|
|
|
33
33
|
// copy VM
|
|
34
34
|
return Task.run(
|
|
35
35
|
{
|
|
36
|
-
name: 'health check',
|
|
36
|
+
properties: { name: 'health check' },
|
|
37
37
|
},
|
|
38
38
|
async () => {
|
|
39
39
|
const { $xapi: xapi } = sr
|
|
@@ -47,12 +47,12 @@ export const MixinXapiWriter = (BaseClass = Object) =>
|
|
|
47
47
|
}
|
|
48
48
|
if (await this.#isAlreadyOnHealthCheckSr(baseVm)) {
|
|
49
49
|
healthCheckVmRef = await Task.run(
|
|
50
|
-
{ name: 'cloning-vm' },
|
|
50
|
+
{ properties: { name: 'cloning-vm' } },
|
|
51
51
|
async () => await xapi.callAsync('VM.clone', this._targetVmRef, `Health Check - ${baseVm.name_label}`)
|
|
52
52
|
)
|
|
53
53
|
} else {
|
|
54
54
|
healthCheckVmRef = await Task.run(
|
|
55
|
-
{ name: 'copying-vm' },
|
|
55
|
+
{ properties: { name: 'copying-vm' } },
|
|
56
56
|
async () =>
|
|
57
57
|
await xapi.callAsync(
|
|
58
58
|
'VM.copy',
|
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.72.0",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=14.18"
|
|
14
14
|
},
|
|
@@ -20,21 +20,22 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@iarna/toml": "^2.2.5",
|
|
22
22
|
"@kldzj/stream-throttle": "^1.1.1",
|
|
23
|
-
"@vates/async-each": "^1.0.
|
|
23
|
+
"@vates/async-each": "^1.0.3",
|
|
24
24
|
"@vates/cached-dns.lookup": "^1.0.0",
|
|
25
25
|
"@vates/compose": "^2.1.0",
|
|
26
26
|
"@vates/decorate-with": "^2.1.0",
|
|
27
27
|
"@vates/disposable": "^0.1.6",
|
|
28
28
|
"@vates/fuse-vhd": "^2.1.2",
|
|
29
|
-
"@vates/generator-toolbox": "^1.1.
|
|
30
|
-
"@vates/nbd-client": "^3.
|
|
29
|
+
"@vates/generator-toolbox": "^1.1.2",
|
|
30
|
+
"@vates/nbd-client": "^3.4.0",
|
|
31
31
|
"@vates/parse-duration": "^0.1.1",
|
|
32
|
-
"@
|
|
33
|
-
"@xen-orchestra/
|
|
34
|
-
"@xen-orchestra/
|
|
35
|
-
"@xen-orchestra/
|
|
36
|
-
"@xen-orchestra/
|
|
37
|
-
"@xen-orchestra/
|
|
32
|
+
"@vates/task": "^0.7.0",
|
|
33
|
+
"@xen-orchestra/async-map": "^0.1.3",
|
|
34
|
+
"@xen-orchestra/disk-transform": "^1.2.3",
|
|
35
|
+
"@xen-orchestra/fs": "^4.8.0",
|
|
36
|
+
"@xen-orchestra/log": "^0.7.2",
|
|
37
|
+
"@xen-orchestra/qcow2": "^1.3.0",
|
|
38
|
+
"@xen-orchestra/template": "^0.1.1",
|
|
38
39
|
"app-conf": "^3.0.0",
|
|
39
40
|
"compare-versions": "^6.0.0",
|
|
40
41
|
"d3-time-format": "^4.1.0",
|
|
@@ -42,7 +43,7 @@
|
|
|
42
43
|
"golike-defer": "^0.5.1",
|
|
43
44
|
"human-format": "^1.2.0",
|
|
44
45
|
"limit-concurrency-decorator": "^0.6.0",
|
|
45
|
-
"lodash": "^4.
|
|
46
|
+
"lodash": "^4.18.0",
|
|
46
47
|
"moment-timezone": "^0.5.46",
|
|
47
48
|
"ms": "^2.1.3",
|
|
48
49
|
"node-zone": "^0.4.0",
|
|
@@ -52,8 +53,8 @@
|
|
|
52
53
|
"tar": "^7.5.3",
|
|
53
54
|
"uuid": "^9.0.0",
|
|
54
55
|
"value-matcher": "^0.2.0",
|
|
55
|
-
"vhd-lib": "^4.
|
|
56
|
-
"xen-api": "^4.7.
|
|
56
|
+
"vhd-lib": "^4.16.0",
|
|
57
|
+
"xen-api": "^4.7.7",
|
|
57
58
|
"yazl": "^2.5.1"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"tmp": "^0.2.1"
|
|
64
65
|
},
|
|
65
66
|
"peerDependencies": {
|
|
66
|
-
"@xen-orchestra/xapi": "^8.7.
|
|
67
|
+
"@xen-orchestra/xapi": "^8.7.2"
|
|
67
68
|
},
|
|
68
69
|
"license": "AGPL-3.0-or-later",
|
|
69
70
|
"author": {
|
package/Task.mjs
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import CancelToken from 'promise-toolbox/CancelToken'
|
|
2
|
-
import Zone from 'node-zone'
|
|
3
|
-
|
|
4
|
-
const logAfterEnd = log => {
|
|
5
|
-
const error = new Error('task has already ended')
|
|
6
|
-
error.log = log
|
|
7
|
-
throw error
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const noop = Function.prototype
|
|
11
|
-
|
|
12
|
-
const serializeErrors = errors => (Array.isArray(errors) ? errors.map(serializeError) : errors)
|
|
13
|
-
|
|
14
|
-
// Create a serializable object from an error.
|
|
15
|
-
//
|
|
16
|
-
// Otherwise some fields might be non-enumerable and missing from logs.
|
|
17
|
-
const serializeError = error =>
|
|
18
|
-
error instanceof Error
|
|
19
|
-
? {
|
|
20
|
-
...error, // Copy enumerable properties.
|
|
21
|
-
code: error.code,
|
|
22
|
-
errors: serializeErrors(error.errors), // supports AggregateError
|
|
23
|
-
message: error.message,
|
|
24
|
-
name: error.name,
|
|
25
|
-
stack: error.stack,
|
|
26
|
-
}
|
|
27
|
-
: error
|
|
28
|
-
|
|
29
|
-
const $$task = Symbol('@xen-orchestra/backups/Task')
|
|
30
|
-
|
|
31
|
-
export class Task {
|
|
32
|
-
static get cancelToken() {
|
|
33
|
-
const task = Zone.current.data[$$task]
|
|
34
|
-
return task !== undefined ? task.#cancelToken : CancelToken.none
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
static run(opts, fn) {
|
|
38
|
-
return new this(opts).run(fn, true)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
static wrapFn(opts, fn) {
|
|
42
|
-
// compatibility with @decorateWith
|
|
43
|
-
if (typeof fn !== 'function') {
|
|
44
|
-
;[fn, opts] = [opts, fn]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return function () {
|
|
48
|
-
return Task.run(typeof opts === 'function' ? opts.apply(this, arguments) : opts, () => fn.apply(this, arguments))
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
#cancelToken
|
|
53
|
-
#id = Math.random().toString(36).slice(2)
|
|
54
|
-
#onLog
|
|
55
|
-
#zone
|
|
56
|
-
|
|
57
|
-
constructor({ name, data, onLog }) {
|
|
58
|
-
let parentCancelToken, parentId
|
|
59
|
-
if (onLog === undefined) {
|
|
60
|
-
const parent = Zone.current.data[$$task]
|
|
61
|
-
if (parent === undefined) {
|
|
62
|
-
onLog = noop
|
|
63
|
-
} else {
|
|
64
|
-
onLog = log => parent.#onLog(log)
|
|
65
|
-
parentCancelToken = parent.#cancelToken
|
|
66
|
-
parentId = parent.#id
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const zone = Zone.current.fork('@xen-orchestra/backups/Task')
|
|
71
|
-
zone.data[$$task] = this
|
|
72
|
-
this.#zone = zone
|
|
73
|
-
|
|
74
|
-
const { cancel, token } = CancelToken.source(parentCancelToken && [parentCancelToken])
|
|
75
|
-
this.#cancelToken = token
|
|
76
|
-
this.cancel = cancel
|
|
77
|
-
|
|
78
|
-
this.#onLog = onLog
|
|
79
|
-
|
|
80
|
-
this.#log('start', {
|
|
81
|
-
data,
|
|
82
|
-
message: name,
|
|
83
|
-
parentId,
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
failure(error) {
|
|
88
|
-
this.#end('failure', serializeError(error))
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
info(message, data) {
|
|
92
|
-
this.#log('info', { data, message })
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Run a function in the context of this task
|
|
97
|
-
*
|
|
98
|
-
* In case of error, the task will be failed.
|
|
99
|
-
*
|
|
100
|
-
* @typedef Result
|
|
101
|
-
* @param {() => Result} fn
|
|
102
|
-
* @param {boolean} last - Whether the task should succeed if there is no error
|
|
103
|
-
* @returns Result
|
|
104
|
-
*/
|
|
105
|
-
run(fn, last = false) {
|
|
106
|
-
return this.#zone.run(() => {
|
|
107
|
-
try {
|
|
108
|
-
const result = fn()
|
|
109
|
-
let then
|
|
110
|
-
if (result != null && typeof (then = result.then) === 'function') {
|
|
111
|
-
then.call(result, last && (value => this.success(value)), error => this.failure(error))
|
|
112
|
-
} else if (last) {
|
|
113
|
-
this.success(result)
|
|
114
|
-
}
|
|
115
|
-
return result
|
|
116
|
-
} catch (error) {
|
|
117
|
-
this.failure(error)
|
|
118
|
-
throw error
|
|
119
|
-
}
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
success(value) {
|
|
124
|
-
this.#end('success', value)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
warning(message, data) {
|
|
128
|
-
this.#log('warning', { data, message })
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
wrapFn(fn, last) {
|
|
132
|
-
const task = this
|
|
133
|
-
return function () {
|
|
134
|
-
return task.run(() => fn.apply(this, arguments), last)
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
#end(status, result) {
|
|
139
|
-
this.#log('end', { result, status })
|
|
140
|
-
this.#onLog = logAfterEnd
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
#log(event, props) {
|
|
144
|
-
this.#onLog({
|
|
145
|
-
...props,
|
|
146
|
-
event,
|
|
147
|
-
taskId: this.#id,
|
|
148
|
-
timestamp: Date.now(),
|
|
149
|
-
})
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
for (const method of ['info', 'warning']) {
|
|
154
|
-
Task[method] = (...args) => Zone.current.data[$$task]?.[method](...args)
|
|
155
|
-
}
|