@xen-orchestra/backups 0.46.0 → 0.47.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/_backupWorker.mjs CHANGED
@@ -2,7 +2,10 @@ import { createLogger } from '@xen-orchestra/log'
2
2
  import { catchGlobalErrors } from '@xen-orchestra/log/configure'
3
3
 
4
4
  import Disposable from 'promise-toolbox/Disposable'
5
+ import humanFormat from 'human-format'
5
6
  import ignoreErrors from 'promise-toolbox/ignoreErrors'
7
+ import mapValues from 'lodash/mapValues.js'
8
+ import ms from 'ms'
6
9
  import { compose } from '@vates/compose'
7
10
  import { createCachedLookup } from '@vates/cached-dns.lookup'
8
11
  import { createDebounceResource } from '@vates/disposable/debounceResource.js'
@@ -20,7 +23,7 @@ createCachedLookup().patchGlobal()
20
23
 
21
24
  const logger = createLogger('xo:backups:worker')
22
25
  catchGlobalErrors(logger)
23
- const { debug } = logger
26
+ const { debug, info } = logger
24
27
 
25
28
  class BackupWorker {
26
29
  #config
@@ -149,6 +152,27 @@ process.on('message', async message => {
149
152
  debug('message received', { message })
150
153
 
151
154
  if (message.action === 'run') {
155
+ const resourceStart = process.resourceUsage()
156
+ const timeStart = process.hrtime.bigint()
157
+ info('starting backup')
158
+
159
+ process.on('exit', exitCode => {
160
+ const resourceUsage = mapValues(process.resourceUsage(), (end, key) => end - resourceStart[key])
161
+ const cpuTotal = resourceUsage.userCPUTime + resourceUsage.systemCPUTime
162
+ const duration = Number((process.hrtime.bigint() - timeStart) / 1000n) // in μs
163
+
164
+ info('process will exit', {
165
+ duration,
166
+ exitCode,
167
+ resourceUsage,
168
+ summary: {
169
+ duration: ms(duration / 1000),
170
+ cpuUsage: Math.round((100 * cpuTotal) / duration) + '%',
171
+ memoryUsage: humanFormat.bytes(resourceUsage.maxRSS * 1024),
172
+ },
173
+ })
174
+ })
175
+
152
176
  const backupWorker = new BackupWorker(message.data)
153
177
  try {
154
178
  const result = message.runWithLogs
@@ -177,6 +201,7 @@ process.on('message', async message => {
177
201
  status: 'failure',
178
202
  })
179
203
  } finally {
204
+ info('backup has ended')
180
205
  await ignoreErrors.call(backupWorker.debounceResource.flushAll())
181
206
  process.disconnect()
182
207
  }
@@ -1,5 +1,3 @@
1
- import { decorateMethodsWith } from '@vates/decorate-with'
2
- import { defer } from 'golike-defer'
3
1
  import { AbstractRemote } from './_AbstractRemote.mjs'
4
2
  import { FullRemoteWriter } from '../_writers/FullRemoteWriter.mjs'
5
3
  import { forkStreamUnpipe } from '../_forkStreamUnpipe.mjs'
@@ -10,15 +8,9 @@ export const FullRemote = class FullRemoteVmBackupRunner extends AbstractRemote
10
8
  _getRemoteWriter() {
11
9
  return FullRemoteWriter
12
10
  }
13
- async _run($defer) {
11
+ async _run() {
14
12
  const transferList = await this._computeTransferList(({ mode }) => mode === 'full')
15
13
 
16
- await this._callWriters(async writer => {
17
- await writer.beforeBackup()
18
- $defer(async () => {
19
- await writer.afterBackup()
20
- })
21
- }, 'writer.beforeBackup()')
22
14
  if (transferList.length > 0) {
23
15
  for (const metadata of transferList) {
24
16
  const stream = await this._sourceRemoteAdapter.readFullVmBackup(metadata)
@@ -46,7 +38,3 @@ export const FullRemote = class FullRemoteVmBackupRunner extends AbstractRemote
46
38
  }
47
39
  }
48
40
  }
49
-
50
- decorateMethodsWith(FullRemote, {
51
- _run: defer,
52
- })
@@ -1,6 +1,4 @@
1
1
  import { asyncEach } from '@vates/async-each'
2
- import { decorateMethodsWith } from '@vates/decorate-with'
3
- import { defer } from 'golike-defer'
4
2
  import assert from 'node:assert'
5
3
  import * as UUID from 'uuid'
6
4
  import isVhdDifferencingDisk from 'vhd-lib/isVhdDifferencingDisk.js'
@@ -52,14 +50,8 @@ class IncrementalRemoteVmBackupRunner extends AbstractRemote {
52
50
  })
53
51
  // yeah , let's go
54
52
  }
55
- async _run($defer) {
53
+ async _run() {
56
54
  const transferList = await this._computeTransferList(({ mode }) => mode === 'delta')
57
- await this._callWriters(async writer => {
58
- await writer.beforeBackup()
59
- $defer(async () => {
60
- await writer.afterBackup()
61
- })
62
- }, 'writer.beforeBackup()')
63
55
 
64
56
  if (transferList.length > 0) {
65
57
  for (const metadata of transferList) {
@@ -110,6 +102,3 @@ class IncrementalRemoteVmBackupRunner extends AbstractRemote {
110
102
  }
111
103
 
112
104
  export const IncrementalRemote = IncrementalRemoteVmBackupRunner
113
- decorateMethodsWith(IncrementalRemoteVmBackupRunner, {
114
- _run: defer,
115
- })
@@ -1,4 +1,6 @@
1
1
  import { asyncEach } from '@vates/async-each'
2
+ import { decorateMethodsWith } from '@vates/decorate-with'
3
+ import { defer } from 'golike-defer'
2
4
  import { Disposable } from 'promise-toolbox'
3
5
 
4
6
  import { getVmBackupDir } from '../../_getVmBackupDir.mjs'
@@ -90,11 +92,21 @@ export const AbstractRemote = class AbstractRemoteVmBackupRunner extends Abstrac
90
92
  return chain
91
93
  }
92
94
 
93
- async run() {
95
+ async run($defer) {
94
96
  const handler = this._sourceRemoteAdapter._handler
95
97
  await Disposable.use(await handler.lock(getVmBackupDir(this._vmUuid)), async () => {
98
+ await this._callWriters(async writer => {
99
+ await writer.beforeBackup()
100
+ $defer(async () => {
101
+ await writer.afterBackup()
102
+ })
103
+ }, 'writer.beforeBackup()')
96
104
  await this._run()
97
105
  await this._healthCheck()
98
106
  })
99
107
  }
100
108
  }
109
+
110
+ decorateMethodsWith(AbstractRemote, {
111
+ run: defer,
112
+ })
@@ -100,7 +100,12 @@ export const MixinRemoteWriter = (BaseClass = Object) =>
100
100
  additionnalVmTag: 'xo:no-bak=Health Check',
101
101
  },
102
102
  }).run()
103
- const restoredVm = xapi.getObject(restoredId)
103
+ let restoredVm
104
+ try {
105
+ restoredVm = xapi.getObject(restoredId)
106
+ } catch (err) {
107
+ restoredVm = await xapi.waitObject(restoredId)
108
+ }
104
109
  try {
105
110
  await new HealthCheckVmBackup({
106
111
  restoredVm,
@@ -26,7 +26,8 @@ export const MixinXapiWriter = (BaseClass = Object) =>
26
26
  }
27
27
 
28
28
  healthCheck() {
29
- const sr = this._healthCheckSr
29
+ // the SR that the VM has been replicated on
30
+ const sr = this._sr
30
31
  assert.notStrictEqual(sr, undefined, 'SR should be defined before making a health check')
31
32
  assert.notEqual(this._targetVmRef, undefined, 'A vm should have been transfered to be health checked')
32
33
  // copy VM
@@ -38,8 +39,12 @@ export const MixinXapiWriter = (BaseClass = Object) =>
38
39
  const { $xapi: xapi } = sr
39
40
  let healthCheckVmRef
40
41
  try {
41
- const baseVm = xapi.getObject(this._targetVmRef) ?? (await xapi.waitObject(this._targetVmRef))
42
-
42
+ let baseVm
43
+ try {
44
+ baseVm = xapi.getObject(this._targetVmRef)
45
+ } catch (err) {
46
+ baseVm = await xapi.waitObject(this._targetVmRef)
47
+ }
43
48
  if (await this.#isAlreadyOnHealthCheckSr(baseVm)) {
44
49
  healthCheckVmRef = await Task.run(
45
50
  { name: 'cloning-vm' },
@@ -53,11 +58,17 @@ export const MixinXapiWriter = (BaseClass = Object) =>
53
58
  { name: 'copying-vm' },
54
59
  async () =>
55
60
  await xapi
56
- .callAsync('VM.copy', this._targetVmRef, `Health Check - ${baseVm.name_label}`, sr.$ref)
61
+ .callAsync(
62
+ 'VM.copy',
63
+ this._targetVmRef,
64
+ `Health Check - ${baseVm.name_label}`,
65
+ this._healthCheckSr.$ref
66
+ )
57
67
  .then(extractOpaqueRef)
58
68
  )
59
69
  }
60
- const healthCheckVm = xapi.getObject(healthCheckVmRef) ?? (await xapi.waitObject(healthCheckVmRef))
70
+ const healthCheckVm =
71
+ xapi.getObject(healthCheckVmRef, undefined) ?? (await xapi.waitObject(healthCheckVmRef))
61
72
  await healthCheckVm.add_tags('xo:no-bak=Health Check')
62
73
  await new HealthCheckVmBackup({
63
74
  restoredVm: healthCheckVm,
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.46.0",
11
+ "version": "0.47.0",
12
12
  "engines": {
13
13
  "node": ">=14.18"
14
14
  },
@@ -36,15 +36,17 @@
36
36
  "d3-time-format": "^4.1.0",
37
37
  "decorator-synchronized": "^0.6.0",
38
38
  "golike-defer": "^0.5.1",
39
+ "human-format": "^1.2.0",
39
40
  "limit-concurrency-decorator": "^0.5.0",
40
41
  "lodash": "^4.17.20",
42
+ "ms": "^2.1.3",
41
43
  "node-zone": "^0.4.0",
42
44
  "parse-pairs": "^2.0.0",
43
45
  "promise-toolbox": "^0.21.0",
44
46
  "proper-lockfile": "^4.1.2",
45
47
  "tar": "^6.1.15",
46
48
  "uuid": "^9.0.0",
47
- "vhd-lib": "^4.9.1",
49
+ "vhd-lib": "^4.9.2",
48
50
  "xen-api": "^3.0.0",
49
51
  "yazl": "^2.5.1"
50
52
  },