@xen-orchestra/backups 0.71.2 → 0.71.3

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.
@@ -65,8 +65,9 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
65
65
  snapshotCandidates,
66
66
  async snapshot => {
67
67
  let diffDisk
68
+ let activeVdi
68
69
  try {
69
- const activeVdi = sr.$xapi.getObject(snapshot.$snapshot_of)
70
+ activeVdi = sr.$xapi.getObject(snapshot.$snapshot_of)
70
71
  const userVbds = activeVdi.$VBDs?.filter(vbd => vbd.$VM && !vbd.$VM.is_control_domain) ?? []
71
72
  if (userVbds.length !== 1) {
72
73
  debug('checkBaseVdis, share vbd ', { ref: snapshot.$ref, userVbds })
@@ -81,7 +82,12 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
81
82
  // but it indicates the users played with the blocked operations
82
83
  return
83
84
  }
84
- diffDisk = new XapiDiskSource({ xapi: sr.$xapi, vdiRef: activeVdi.$ref, baseRef: snapshot.$ref })
85
+ diffDisk = new XapiDiskSource({
86
+ xapi: sr.$xapi,
87
+ vdiRef: activeVdi.$ref,
88
+ baseRef: snapshot.$ref,
89
+ onlyListChangedBlocks: true,
90
+ })
85
91
  await diffDisk.init()
86
92
  if (diffDisk.getBlockIndexes().length === 0) {
87
93
  const sourceUuid = snapshot.other_config?.[COPY_OF]
@@ -103,6 +109,10 @@ export class IncrementalXapiWriter extends MixinXapiWriter(AbstractIncrementalWr
103
109
  return
104
110
  } finally {
105
111
  await diffDisk?.close().catch(error => debug('checkBaseVdis, error closing', error))
112
+ await sr.$xapi.VDI_disconnectFromControlDomain(snapshot.$ref)
113
+ if (activeVdi !== undefined) {
114
+ await sr.$xapi.VDI_disconnectFromControlDomain(activeVdi.$ref)
115
+ }
106
116
  }
107
117
  },
108
118
  {
@@ -10,6 +10,7 @@ import { createLogger } from '@xen-orchestra/log'
10
10
 
11
11
  import { basename, dirname } from 'path'
12
12
  import { asyncEach } from '@vates/async-each'
13
+ import { relativeFromFile } from '@xen-orchestra/fs/path'
13
14
 
14
15
  // @ts-ignore
15
16
  const { warn } = createLogger('remote-disk:merge')
@@ -18,6 +19,7 @@ const { warn } = createLogger('remote-disk:merge')
18
19
  * @typedef {Object} MergeState
19
20
  * @property {{ uuid: string }} child
20
21
  * @property {{ uuid: string }} parent
22
+ * @property { string[] | undefined} chain
21
23
  * @property {number} currentBlock
22
24
  * @property {number} mergedDataSize
23
25
  * @property {'mergeBlocks' | 'cleanup'} step
@@ -32,6 +34,7 @@ export class MergeRemoteDisk {
32
34
  #state = {
33
35
  child: { uuid: '0' },
34
36
  parent: { uuid: '0' },
37
+ chain: undefined,
35
38
  currentBlock: 0,
36
39
  mergedDataSize: 0,
37
40
  step: 'mergeBlocks',
@@ -205,6 +208,9 @@ export class MergeRemoteDisk {
205
208
  } else {
206
209
  this.#state.child = { uuid: childDisk.getUuid() ?? undefined }
207
210
  this.#state.parent = { uuid: parentDisk.getUuid() ?? undefined }
211
+ this.#state.chain = [parentDisk.getPath(), ...childDisk.getPaths()].map(path =>
212
+ relativeFromFile(this.#statePath, path)
213
+ )
208
214
 
209
215
  // Finds first allocated block for the 2 following loops
210
216
  while (this.#state.currentBlock < getMaxBlockCount && !childDisk.hasBlock(this.#state.currentBlock)) {
@@ -55,6 +55,16 @@ export class RemoteDisk extends RandomAccessDisk {
55
55
  throw new Error(`getPath must be implemented`)
56
56
  }
57
57
 
58
+ /**
59
+ * Abstract
60
+ * Returns an array of disk paths.
61
+ *
62
+ * @returns {string[]}
63
+ */
64
+ getPaths() {
65
+ throw new Error(`getPaths must be implemented`)
66
+ }
67
+
58
68
  /**
59
69
  * Abstract
60
70
  * @returns {string}
@@ -16,6 +16,7 @@ import { DISK_TYPES } from 'vhd-lib/_constants.js'
16
16
  import { isVhdAlias, resolveVhdAlias } from 'vhd-lib/aliases.js'
17
17
  import { stringify } from 'uuid'
18
18
  import { dirname, join } from 'node:path'
19
+ import { RemoteVhdDiskChain } from './RemoteVhdDiskChain.mjs'
19
20
 
20
21
  export class RemoteVhdDisk extends RemoteDisk {
21
22
  /**
@@ -140,6 +141,15 @@ export class RemoteVhdDisk extends RemoteDisk {
140
141
  return this.#path
141
142
  }
142
143
 
144
+ /**
145
+ * Returns the disk path in an array.
146
+ *
147
+ * @returns {string[]}
148
+ */
149
+ getPaths() {
150
+ return [this.getPath()]
151
+ }
152
+
143
153
  /**
144
154
  * @returns {string}
145
155
  */
@@ -259,7 +269,11 @@ export class RemoteVhdDisk extends RemoteDisk {
259
269
  * @returns {Promise<number>} blockSize
260
270
  */
261
271
  async mergeBlock(childDisk, index, isResumingMerge) {
262
- if ((await this.isDirectory()) && childDisk instanceof RemoteVhdDisk && (await childDisk.isDirectory())) {
272
+ if (
273
+ (childDisk instanceof RemoteVhdDisk || childDisk instanceof RemoteVhdDiskChain) &&
274
+ (await this.isDirectory()) &&
275
+ (await childDisk.isDirectory())
276
+ ) {
263
277
  try {
264
278
  await this.#handler.rename(childDisk.getBlockPath(index), this.getBlockPath(index))
265
279
 
@@ -123,6 +123,15 @@ export class RemoteVhdDiskChain extends RemoteDisk {
123
123
  return this.#disks[this.#disks.length - 1].getPath()
124
124
  }
125
125
 
126
+ /**
127
+ * Disk chains return an array of disk paths.
128
+ *
129
+ * @returns {string[]}
130
+ */
131
+ getPaths() {
132
+ return this.#disks.map(disk => disk.getPath())
133
+ }
134
+
126
135
  /**
127
136
  * @returns {string}
128
137
  */
@@ -134,12 +143,7 @@ export class RemoteVhdDiskChain extends RemoteDisk {
134
143
  * @returns {Promise<boolean>} canMergeConcurently
135
144
  */
136
145
  async canMergeConcurently() {
137
- for (const disk of this.#disks) {
138
- if (!(await disk.isDirectory())) {
139
- return true
140
- }
141
- }
142
- return false
146
+ return this.isDirectory()
143
147
  }
144
148
 
145
149
  /**
@@ -221,6 +225,21 @@ export class RemoteVhdDiskChain extends RemoteDisk {
221
225
  throw new Error(`Can't merge block into a disk chain`)
222
226
  }
223
227
 
228
+ /**
229
+ * Gets a specific block path from the VHD directory disk.
230
+ * @param {number} index
231
+ * @returns {string} blockPath
232
+ */
233
+ getBlockPath(index) {
234
+ for (const disk of [...this.#disks].reverse()) {
235
+ if (disk.hasBlock(index)) {
236
+ return disk.getBlockPath(index)
237
+ }
238
+ }
239
+
240
+ throw new Error(`Block ${index} not found in chain`)
241
+ }
242
+
224
243
  /**
225
244
  * @returns {VhdFooter}
226
245
  */
@@ -269,4 +288,17 @@ export class RemoteVhdDiskChain extends RemoteDisk {
269
288
  await disk.unlink()
270
289
  }
271
290
  }
291
+
292
+ /**
293
+ * Check if all the disks in the chain are VHD directories.
294
+ * @returns {Promise<boolean>}
295
+ */
296
+ async isDirectory() {
297
+ for (const disk of this.#disks) {
298
+ if (!(await disk.isDirectory())) {
299
+ return false
300
+ }
301
+ }
302
+ return true
303
+ }
272
304
  }
package/package.json CHANGED
@@ -8,12 +8,13 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/vatesfr/xen-orchestra.git"
10
10
  },
11
- "version": "0.71.2",
11
+ "version": "0.71.3",
12
12
  "engines": {
13
13
  "node": ">=14.18"
14
14
  },
15
15
  "scripts": {
16
16
  "postversion": "npm publish --access public",
17
+ "test": "node --test",
17
18
  "test-integration": "node --test *.integ.mjs"
18
19
  },
19
20
  "dependencies": {
@@ -62,7 +63,7 @@
62
63
  "tmp": "^0.2.1"
63
64
  },
64
65
  "peerDependencies": {
65
- "@xen-orchestra/xapi": "^8.7.0"
66
+ "@xen-orchestra/xapi": "^8.7.1"
66
67
  },
67
68
  "license": "AGPL-3.0-or-later",
68
69
  "author": {