@xen-orchestra/backups 0.72.1 → 0.73.1
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/ImportVmBackup.mjs +1 -1
- package/RemoteAdapter.mjs +8 -6
- package/_otherConfig.mjs +2 -0
- package/_runners/_vmRunners/IncrementalXapi.mjs +115 -41
- package/_runners/_vmRunners/_AbstractXapi.mjs +38 -34
- package/_runners/_writers/IncrementalXapiWriter.mjs +92 -27
- package/_runners/_writers/_MixinRemoteWriter.mjs +0 -2
- package/package.json +8 -6
- package/tests.fixtures.d.mts +47 -0
- package/_cleanVm.mjs +0 -623
- package/disks/MergeRemoteDisk.mjs +0 -324
- package/disks/RemoteDisk.mjs +0 -223
- package/disks/RemoteVhdDisk.mjs +0 -480
- package/disks/RemoteVhdDiskChain.mjs +0 -304
- package/disks/index.mjs +0 -49
- package/disks/openDiskChain.mjs +0 -40
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('./RemoteVhdDisk.mjs').VhdFooter} VhdFooter
|
|
5
|
-
* @typedef {import('./RemoteVhdDisk.mjs').RemoteVhdDisk} RemoteVhdDisk
|
|
6
|
-
* @typedef {import('@xen-orchestra/disk-transform').DiskBlock} DiskBlock
|
|
7
|
-
* @typedef {import('@xen-orchestra/disk-transform').FileAccessor} FileAccessor
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { RemoteDisk } from './RemoteDisk.mjs'
|
|
11
|
-
|
|
12
|
-
export class RemoteVhdDiskChain extends RemoteDisk {
|
|
13
|
-
/**
|
|
14
|
-
* @type {RemoteVhdDisk[]}
|
|
15
|
-
*/
|
|
16
|
-
#disks
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @type {number}
|
|
20
|
-
*/
|
|
21
|
-
#blockSize = 2 * 1024 * 1024
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @type {number}
|
|
25
|
-
*/
|
|
26
|
-
#headerSize = 1024
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @type {number}
|
|
30
|
-
*/
|
|
31
|
-
#footerSize = 512
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @type {number}
|
|
35
|
-
*/
|
|
36
|
-
#bitmapSize = 512
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @param {Object} params
|
|
40
|
-
* @param {FileAccessor} params.handler
|
|
41
|
-
* @param {RemoteVhdDisk[]} params.disks
|
|
42
|
-
*/
|
|
43
|
-
constructor({ disks }) {
|
|
44
|
-
super()
|
|
45
|
-
this.#disks = disks
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Initializes the VHD chain
|
|
50
|
-
* @param {Object} options
|
|
51
|
-
* @param {boolean} options.force
|
|
52
|
-
* @returns {Promise<void>}
|
|
53
|
-
*/
|
|
54
|
-
async init(options) {
|
|
55
|
-
try {
|
|
56
|
-
await Promise.all(this.#disks.map(disk => disk.init(options)))
|
|
57
|
-
let parentUuid = ''
|
|
58
|
-
for (const [index, disk] of this.#disks.entries()) {
|
|
59
|
-
if (index !== 0) {
|
|
60
|
-
if (!disk.isDifferencing()) {
|
|
61
|
-
throw Object.assign(new Error("Can't init vhd directory with non differencing child disks"), {
|
|
62
|
-
code: 'NOT_SUPPORTED',
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
if (disk.getParentUuid() !== parentUuid) {
|
|
66
|
-
throw Object.assign(new Error("Can't init vhd directory with incorrect parentage"), {
|
|
67
|
-
code: 'NOT_SUPPORTED',
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
parentUuid = disk.getUuid()
|
|
73
|
-
}
|
|
74
|
-
} catch (error) {
|
|
75
|
-
await this.close()
|
|
76
|
-
throw error
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Closes the VHD.
|
|
82
|
-
* @returns {Promise<void>}
|
|
83
|
-
*/
|
|
84
|
-
async close() {
|
|
85
|
-
await Promise.all(this.#disks.map(disk => disk.close()))
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* @returns {number}
|
|
90
|
-
*/
|
|
91
|
-
getVirtualSize() {
|
|
92
|
-
return this.#disks[this.#disks.length - 1].getVirtualSize()
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* @returns {number} size
|
|
97
|
-
*/
|
|
98
|
-
getSizeOnDisk() {
|
|
99
|
-
const batEntrySize = 4
|
|
100
|
-
const sectorSize = 512
|
|
101
|
-
const batSize = Math.ceil((this.getMaxBlockCount() * batEntrySize) / sectorSize) * sectorSize
|
|
102
|
-
|
|
103
|
-
return (
|
|
104
|
-
this.#footerSize +
|
|
105
|
-
this.#headerSize +
|
|
106
|
-
batSize +
|
|
107
|
-
this.getBlockIndexes().length * (this.#blockSize + this.#bitmapSize) +
|
|
108
|
-
this.#footerSize
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* @returns {number}
|
|
114
|
-
*/
|
|
115
|
-
getBlockSize() {
|
|
116
|
-
return this.#disks[this.#disks.length - 1].getBlockSize()
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @returns {string}
|
|
121
|
-
*/
|
|
122
|
-
getPath() {
|
|
123
|
-
return this.#disks[this.#disks.length - 1].getPath()
|
|
124
|
-
}
|
|
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
|
-
|
|
135
|
-
/**
|
|
136
|
-
* @returns {string}
|
|
137
|
-
*/
|
|
138
|
-
getUuid() {
|
|
139
|
-
return this.#disks[this.#disks.length - 1].getUuid()
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* @returns {Promise<boolean>} canMergeConcurently
|
|
144
|
-
*/
|
|
145
|
-
async canMergeConcurently() {
|
|
146
|
-
return this.isDirectory()
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* @returns {number} getMaxBlockCount
|
|
151
|
-
*/
|
|
152
|
-
getMaxBlockCount() {
|
|
153
|
-
return this.#disks[this.#disks.length - 1].getMaxBlockCount()
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Checks if the VHD contains a specific block.
|
|
158
|
-
* @param {number} index
|
|
159
|
-
* @returns {boolean}
|
|
160
|
-
*/
|
|
161
|
-
hasBlock(index) {
|
|
162
|
-
for (const disk of this.#disks) {
|
|
163
|
-
if (disk.hasBlock(index)) {
|
|
164
|
-
return true
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return false
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Gets the indexes of all blocks in the VHD.
|
|
173
|
-
* @returns {Array<number>}
|
|
174
|
-
*/
|
|
175
|
-
getBlockIndexes() {
|
|
176
|
-
const indexes = new Set()
|
|
177
|
-
for (const disk of this.#disks) {
|
|
178
|
-
for (const index of disk.getBlockIndexes()) {
|
|
179
|
-
indexes.add(index)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return [...indexes]
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Returns the parent non inizialized instance
|
|
187
|
-
* @returns {RemoteDisk}
|
|
188
|
-
*/
|
|
189
|
-
instantiateParent() {
|
|
190
|
-
return this.#disks[0].instantiateParent()
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Writes a full block into this VHD.
|
|
195
|
-
* @param {DiskBlock} diskBlock
|
|
196
|
-
* @return {Promise<number>} blockSize
|
|
197
|
-
*/
|
|
198
|
-
async writeBlock(diskBlock) {
|
|
199
|
-
throw new Error(`Can't write blocks into a disk chain`)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Reads a specific block from the VHD.
|
|
204
|
-
* @param {number} index
|
|
205
|
-
* @returns {Promise<DiskBlock>} diskBlock
|
|
206
|
-
*/
|
|
207
|
-
async readBlock(index) {
|
|
208
|
-
const reversedDisks = this.#disks.slice().reverse()
|
|
209
|
-
for (const disk of reversedDisks) {
|
|
210
|
-
if (disk.hasBlock(index)) {
|
|
211
|
-
return disk.readBlock(index)
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
throw new Error(`Block ${index} not found in chain `)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Reads a specific block from the child disk to copy/move it to this disk.
|
|
219
|
-
* @param {RemoteDisk} childDisk
|
|
220
|
-
* @param {number} index
|
|
221
|
-
* @param {boolean} isResumingMerge
|
|
222
|
-
* @returns {Promise<number>} blockSize
|
|
223
|
-
*/
|
|
224
|
-
async mergeBlock(childDisk, index, isResumingMerge) {
|
|
225
|
-
throw new Error(`Can't merge block into a disk chain`)
|
|
226
|
-
}
|
|
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
|
-
|
|
243
|
-
/**
|
|
244
|
-
* @returns {VhdFooter}
|
|
245
|
-
*/
|
|
246
|
-
getMetadata() {
|
|
247
|
-
return this.#disks[this.#disks.length - 1].getMetadata()
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* @param {RemoteVhdDisk} childDisk
|
|
252
|
-
* @returns {Promise<void>}
|
|
253
|
-
*/
|
|
254
|
-
async flushMetadata(childDisk) {
|
|
255
|
-
throw new Error(`Can't flush metadata on a disk chain`)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* @param {RemoteVhdDisk} childDisk
|
|
260
|
-
* @returns {Promise<void>}
|
|
261
|
-
*/
|
|
262
|
-
mergeMetadata(childDisk) {
|
|
263
|
-
throw new Error(`Can't merge metadata on a disk chain`)
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Checks if the VHD is a differencing disk.
|
|
268
|
-
* @returns {boolean}
|
|
269
|
-
*/
|
|
270
|
-
isDifferencing() {
|
|
271
|
-
return this.#disks[0].isDifferencing()
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Abstract
|
|
276
|
-
* Rename alias/disk
|
|
277
|
-
* @param {string} newPath
|
|
278
|
-
*/
|
|
279
|
-
async rename(newPath) {
|
|
280
|
-
throw new Error(`Can't rename a disk chain`)
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Deletes all the disks
|
|
285
|
-
*/
|
|
286
|
-
async unlink() {
|
|
287
|
-
for (const disk of this.#disks) {
|
|
288
|
-
await disk.unlink()
|
|
289
|
-
}
|
|
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
|
-
}
|
|
304
|
-
}
|
package/disks/index.mjs
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { RemoteVhdDisk } from './RemoteVhdDisk.mjs'
|
|
2
|
-
|
|
3
|
-
export { RemoteDisk } from './RemoteDisk.mjs'
|
|
4
|
-
export { openDiskChain } from './openDiskChain.mjs'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {import('../../disk-transform/src/FileAccessor.mjs').FileAccessor} FileAccessor
|
|
8
|
-
* @typedef {import('./RemoteDisk.mjs').RemoteDisk} RemoteDisk
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @param {Object} params
|
|
13
|
-
* @param {FileAccessor} params.handler
|
|
14
|
-
* @param {string} params.path
|
|
15
|
-
* @returns {Promise<RemoteDisk>}
|
|
16
|
-
*/
|
|
17
|
-
export async function openDisk({ handler, path }) {
|
|
18
|
-
const disk = new RemoteVhdDisk({ handler, path })
|
|
19
|
-
await disk.init()
|
|
20
|
-
return disk
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
*
|
|
24
|
-
* @param {Object} params
|
|
25
|
-
* @param {FileAccessor} params.handler
|
|
26
|
-
* @param {string} params.path
|
|
27
|
-
* @returns {Promise<Disposable<RemoteDisk>>}
|
|
28
|
-
*/
|
|
29
|
-
export async function openDisposableDisk({ handler, path }) {
|
|
30
|
-
const disk = new RemoteVhdDisk({ handler, path })
|
|
31
|
-
await disk.init()
|
|
32
|
-
return {
|
|
33
|
-
value: disk,
|
|
34
|
-
dispose: () => disk.close(),
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const DISK_EXTENSIONS = ['.vhd']
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Returns true if the path points to a supported disk format.
|
|
42
|
-
*
|
|
43
|
-
* @param {FileAccessor} _handler - Remote file handler (reserved for future use)
|
|
44
|
-
* @param {string} path - Path to check
|
|
45
|
-
* @returns {boolean}
|
|
46
|
-
*/
|
|
47
|
-
export function isDisk(_handler, path) {
|
|
48
|
-
return DISK_EXTENSIONS.some(ext => path.endsWith(ext))
|
|
49
|
-
}
|
package/disks/openDiskChain.mjs
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
* @typedef {import('../../disk-transform/src/FileAccessor.mjs').FileAccessor} FileAccessor
|
|
5
|
-
* @typedef {import('./RemoteDisk.mjs').RemoteDisk} RemoteDisk
|
|
6
|
-
*/
|
|
7
|
-
import { DiskChain } from '@xen-orchestra/disk-transform'
|
|
8
|
-
import { RemoteVhdDisk } from './RemoteVhdDisk.mjs'
|
|
9
|
-
|
|
10
|
-
import { defer } from 'golike-defer'
|
|
11
|
-
/**
|
|
12
|
-
* @param {any} $defer
|
|
13
|
-
* @param {Object} params
|
|
14
|
-
* @param {FileAccessor} params.handler
|
|
15
|
-
* @param {string} params.path
|
|
16
|
-
* @param {string | undefined} params.until
|
|
17
|
-
*/
|
|
18
|
-
async function _openDiskChain($defer, { handler, path, until }) {
|
|
19
|
-
let disk
|
|
20
|
-
/**
|
|
21
|
-
* @type {Array<RemoteDisk>}
|
|
22
|
-
*/
|
|
23
|
-
const disks = []
|
|
24
|
-
$defer.onFailure(() => Promise.all(disks.map(disk => disk.close())))
|
|
25
|
-
disk = new RemoteVhdDisk({ handler, path })
|
|
26
|
-
|
|
27
|
-
await disk.init()
|
|
28
|
-
disks.push(disk)
|
|
29
|
-
while (disk.isDifferencing()) {
|
|
30
|
-
disk = await disk.openParent()
|
|
31
|
-
if (disk.getPath() === until) {
|
|
32
|
-
break
|
|
33
|
-
}
|
|
34
|
-
disks.unshift(disk)
|
|
35
|
-
}
|
|
36
|
-
// the root disk
|
|
37
|
-
return new DiskChain({ disks })
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const openDiskChain = defer(_openDiskChain)
|