@xen-orchestra/backups 0.72.0 → 0.73.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/ImportVmBackup.mjs +1 -1
- package/RemoteAdapter.mjs +11 -8
- package/_otherConfig.mjs +2 -0
- package/_runners/_vmRunners/IncrementalXapi.mjs +115 -41
- package/_runners/_vmRunners/_AbstractXapi.mjs +69 -28
- package/_runners/_writers/IncrementalRemoteWriter.mjs +1 -2
- package/_runners/_writers/IncrementalXapiWriter.mjs +172 -77
- package/_runners/_writers/_MixinRemoteWriter.mjs +0 -2
- package/package.json +8 -6
- package/tests.fixtures.d.mts +47 -0
- package/_cleanVm.mjs +0 -628
- package/disks/MergeRemoteDisk.mjs +0 -325
- 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,325 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('./RemoteDisk.mjs').RemoteDisk} RemoteDisk
|
|
5
|
-
* @typedef {import('@xen-orchestra/disk-transform').FileAccessor} FileAccessor
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import assert from 'assert'
|
|
9
|
-
import { createLogger } from '@xen-orchestra/log'
|
|
10
|
-
|
|
11
|
-
import { basename, dirname } from 'path'
|
|
12
|
-
import { asyncEach } from '@vates/async-each'
|
|
13
|
-
import { relativeFromFile } from '@xen-orchestra/fs/path'
|
|
14
|
-
|
|
15
|
-
// @ts-ignore
|
|
16
|
-
const { warn } = createLogger('remote-disk:merge')
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @typedef {Object} MergeState
|
|
20
|
-
* @property {{ uuid: string }} child
|
|
21
|
-
* @property {{ uuid: string }} parent
|
|
22
|
-
* @property { string[] | undefined} chain
|
|
23
|
-
* @property {number} currentBlock
|
|
24
|
-
* @property {number} mergedDataSize
|
|
25
|
-
* @property {'mergeBlocks' | 'cleanup'} step
|
|
26
|
-
* @property {number} diskSize
|
|
27
|
-
* @typedef {(message: string, data?: Record<string, unknown>) => void} Logger
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
export class MergeRemoteDisk {
|
|
31
|
-
/**
|
|
32
|
-
* @type {MergeState}
|
|
33
|
-
*/
|
|
34
|
-
#state = {
|
|
35
|
-
child: { uuid: '0' },
|
|
36
|
-
parent: { uuid: '0' },
|
|
37
|
-
chain: undefined,
|
|
38
|
-
currentBlock: 0,
|
|
39
|
-
mergedDataSize: 0,
|
|
40
|
-
step: 'mergeBlocks',
|
|
41
|
-
diskSize: 0,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @type {string}
|
|
46
|
-
*/
|
|
47
|
-
#statePath = ''
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @type {boolean}
|
|
51
|
-
*/
|
|
52
|
-
#isResuming
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @type {Logger | Function}
|
|
56
|
-
*/
|
|
57
|
-
#logInfo
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @type {number}
|
|
61
|
-
*/
|
|
62
|
-
#mergeBlockConcurrency
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* @type {Function}
|
|
66
|
-
*/
|
|
67
|
-
#onProgress
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @type {boolean}
|
|
71
|
-
*/
|
|
72
|
-
#removeUnused
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* @type {number}
|
|
76
|
-
*/
|
|
77
|
-
#writeStateDelay
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @type {number}
|
|
81
|
-
*/
|
|
82
|
-
#lastStateWrittenAt
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* @type {FileAccessor}
|
|
86
|
-
*/
|
|
87
|
-
#handler
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* @param {FileAccessor} handler
|
|
91
|
-
* @param {Object} params
|
|
92
|
-
* @param {Function} [params.onProgress]
|
|
93
|
-
* @param {Logger | Function} [params.logInfo]
|
|
94
|
-
* @param {boolean} [params.removeUnused]
|
|
95
|
-
* @param {number} [params.mergeBlockConcurrency]
|
|
96
|
-
* @param {number} [params.writeStateDelay]
|
|
97
|
-
*/
|
|
98
|
-
constructor(
|
|
99
|
-
handler,
|
|
100
|
-
{
|
|
101
|
-
onProgress = () => {},
|
|
102
|
-
logInfo = () => {},
|
|
103
|
-
removeUnused = false,
|
|
104
|
-
mergeBlockConcurrency = 2,
|
|
105
|
-
writeStateDelay = 10e3,
|
|
106
|
-
}
|
|
107
|
-
) {
|
|
108
|
-
this.#handler = handler
|
|
109
|
-
this.#logInfo = logInfo
|
|
110
|
-
this.#onProgress = onProgress
|
|
111
|
-
this.#removeUnused = removeUnused
|
|
112
|
-
this.#mergeBlockConcurrency = mergeBlockConcurrency
|
|
113
|
-
this.#writeStateDelay = writeStateDelay
|
|
114
|
-
|
|
115
|
-
this.#isResuming = false
|
|
116
|
-
this.#lastStateWrittenAt = 0
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @param {RemoteDisk} parentDisk
|
|
121
|
-
* @returns {Promise<boolean>} isResuming
|
|
122
|
-
*/
|
|
123
|
-
async isResuming(parentDisk) {
|
|
124
|
-
try {
|
|
125
|
-
await this.#handler.readFile(
|
|
126
|
-
dirname(parentDisk.getPath()) + '/.' + basename(parentDisk.getPath()) + '.merge.json'
|
|
127
|
-
)
|
|
128
|
-
return true
|
|
129
|
-
} catch (error) {
|
|
130
|
-
return false
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* @param {RemoteDisk} parentDisk
|
|
136
|
-
* @param {RemoteDisk} childDisk
|
|
137
|
-
*/
|
|
138
|
-
async merge(parentDisk, childDisk) {
|
|
139
|
-
this.#statePath = dirname(parentDisk.getPath()) + '/.' + basename(parentDisk.getPath()) + '.merge.json'
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
const mergeStateContent = await this.#handler.readFile(this.#statePath)
|
|
143
|
-
this.#state = JSON.parse(mergeStateContent)
|
|
144
|
-
|
|
145
|
-
// work-around a bug introduce in 97d94b795
|
|
146
|
-
//
|
|
147
|
-
// currentBlock could be `null` due to the JSON.stringify of a `NaN` value
|
|
148
|
-
if (this.#state?.currentBlock === null) this.#state.currentBlock = 0
|
|
149
|
-
this.#isResuming = true
|
|
150
|
-
} catch (error) {
|
|
151
|
-
// @ts-ignore
|
|
152
|
-
if (error.code !== 'ENOENT') {
|
|
153
|
-
warn('problem while checking the merge state', { error })
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
/* eslint-disable no-fallthrough */
|
|
159
|
-
switch (this.#state?.step ?? 'mergeBlocks') {
|
|
160
|
-
case 'mergeBlocks':
|
|
161
|
-
await this.#step_mergeBlocks(parentDisk, childDisk)
|
|
162
|
-
case 'cleanup':
|
|
163
|
-
await this.#step_cleanup(parentDisk, childDisk)
|
|
164
|
-
return this.#cleanup(parentDisk, childDisk, true)
|
|
165
|
-
default:
|
|
166
|
-
warn(`Step ${this.#state?.step} is unknown`, { state: this.#state })
|
|
167
|
-
}
|
|
168
|
-
/* eslint-enable no-fallthrough */
|
|
169
|
-
} catch (error) {
|
|
170
|
-
await this.#cleanup(parentDisk, childDisk, false)
|
|
171
|
-
throw error
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async #writeState() {
|
|
176
|
-
try {
|
|
177
|
-
await this.#handler.writeFile(this.#statePath, JSON.stringify(this.#state), { flags: 'w' })
|
|
178
|
-
} catch (err) {
|
|
179
|
-
warn('failed to write merge state', { error: err })
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async #writeStateThrottled() {
|
|
184
|
-
const now = Date.now()
|
|
185
|
-
if (now - this.#lastStateWrittenAt > this.#writeStateDelay) {
|
|
186
|
-
this.#lastStateWrittenAt = now
|
|
187
|
-
await this.#writeState()
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* @param {RemoteDisk} parentDisk
|
|
193
|
-
* @param {RemoteDisk} childDisk
|
|
194
|
-
*/
|
|
195
|
-
async #step_mergeBlocks(parentDisk, childDisk) {
|
|
196
|
-
const getMaxBlockCount = childDisk.getMaxBlockCount()
|
|
197
|
-
await parentDisk.resize(getMaxBlockCount)
|
|
198
|
-
|
|
199
|
-
if (this.#isResuming) {
|
|
200
|
-
const alreadyMergedBlocks = []
|
|
201
|
-
for (let blockId = 0; blockId < this.#state.currentBlock; blockId++) {
|
|
202
|
-
if (childDisk.hasBlock(blockId)) {
|
|
203
|
-
alreadyMergedBlocks.push(blockId)
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
parentDisk.setAllocatedBlocks(alreadyMergedBlocks)
|
|
208
|
-
} else {
|
|
209
|
-
this.#state.child = { uuid: childDisk.getUuid() ?? undefined }
|
|
210
|
-
this.#state.parent = { uuid: parentDisk.getUuid() ?? undefined }
|
|
211
|
-
this.#state.chain = [parentDisk.getPath(), ...childDisk.getPaths()].map(path =>
|
|
212
|
-
relativeFromFile(this.#statePath, path)
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
// Finds first allocated block for the 2 following loops
|
|
216
|
-
while (this.#state.currentBlock < getMaxBlockCount && !childDisk.hasBlock(this.#state.currentBlock)) {
|
|
217
|
-
++this.#state.currentBlock
|
|
218
|
-
}
|
|
219
|
-
await this.#writeState()
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
await this.#mergeBlocks(parentDisk, childDisk)
|
|
223
|
-
await parentDisk.flushMetadata(childDisk)
|
|
224
|
-
await parentDisk.mergeMetadata(childDisk)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* @param {RemoteDisk} parentDisk
|
|
229
|
-
* @param {RemoteDisk} childDisk
|
|
230
|
-
*/
|
|
231
|
-
async #mergeBlocks(parentDisk, childDisk) {
|
|
232
|
-
this.#mergeBlockConcurrency =
|
|
233
|
-
(await parentDisk.canMergeConcurently()) && (await childDisk.canMergeConcurently())
|
|
234
|
-
? this.#mergeBlockConcurrency
|
|
235
|
-
: 1
|
|
236
|
-
|
|
237
|
-
const toMerge = []
|
|
238
|
-
for (let block = this.#state.currentBlock; block < childDisk.getMaxBlockCount(); block++) {
|
|
239
|
-
if (childDisk.hasBlock(block)) {
|
|
240
|
-
toMerge.push(block)
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const nBlocks = toMerge.length
|
|
245
|
-
this.#onProgress({ total: nBlocks, done: 0 })
|
|
246
|
-
|
|
247
|
-
const merging = new Set()
|
|
248
|
-
let counter = 0
|
|
249
|
-
await asyncEach(
|
|
250
|
-
toMerge,
|
|
251
|
-
async blockId => {
|
|
252
|
-
merging.add(blockId)
|
|
253
|
-
|
|
254
|
-
const blockSize = await parentDisk.mergeBlock(childDisk, blockId, this.#isResuming)
|
|
255
|
-
this.#state.mergedDataSize += blockSize
|
|
256
|
-
|
|
257
|
-
this.#state.currentBlock = Math.min(...merging) - 1
|
|
258
|
-
merging.delete(blockId)
|
|
259
|
-
|
|
260
|
-
this.#onProgress({ total: nBlocks, done: counter + 1 })
|
|
261
|
-
counter++
|
|
262
|
-
await this.#writeStateThrottled()
|
|
263
|
-
},
|
|
264
|
-
{ concurrency: this.#mergeBlockConcurrency }
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
await this.#writeState()
|
|
268
|
-
|
|
269
|
-
this.#state.diskSize = childDisk.getSizeOnDisk()
|
|
270
|
-
|
|
271
|
-
this.#onProgress({ total: nBlocks, done: nBlocks })
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* @param {RemoteDisk} parentDisk
|
|
276
|
-
* @param {RemoteDisk} childDisk
|
|
277
|
-
*/
|
|
278
|
-
async #step_cleanup(parentDisk, childDisk) {
|
|
279
|
-
assert.notEqual(this.#state, undefined)
|
|
280
|
-
this.#state.step = 'cleanup'
|
|
281
|
-
await this.#writeState()
|
|
282
|
-
|
|
283
|
-
const mergeTargetPath = childDisk.getPath()
|
|
284
|
-
|
|
285
|
-
// delete intermediate children if needed
|
|
286
|
-
if (this.#removeUnused) {
|
|
287
|
-
await childDisk.unlink()
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
try {
|
|
291
|
-
await parentDisk.rename(mergeTargetPath)
|
|
292
|
-
} catch (error) {
|
|
293
|
-
// @ts-ignore
|
|
294
|
-
if (error.code === 'ENOENT' && this.#isResuming) {
|
|
295
|
-
// @ts-ignore
|
|
296
|
-
this.#logInfo(`the parent disk was already renamed`, {
|
|
297
|
-
parent: parentDisk.getPath(),
|
|
298
|
-
mergeTarget: mergeTargetPath,
|
|
299
|
-
})
|
|
300
|
-
} else {
|
|
301
|
-
throw error
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* @param {RemoteDisk} parentDisk
|
|
308
|
-
* @param {RemoteDisk} childDisk
|
|
309
|
-
* @param {boolean} cleanStateFile
|
|
310
|
-
*
|
|
311
|
-
* @returns {Promise<{mergedDataSize: number, finalDiskSize: number}>} result
|
|
312
|
-
*/
|
|
313
|
-
async #cleanup(parentDisk, childDisk, cleanStateFile) {
|
|
314
|
-
const finalDiskSize = this.#state?.diskSize ?? 0
|
|
315
|
-
const mergedDataSize = this.#state?.mergedDataSize ?? 0
|
|
316
|
-
await parentDisk.close().catch(warn)
|
|
317
|
-
await childDisk.close().catch(warn)
|
|
318
|
-
|
|
319
|
-
if (cleanStateFile) {
|
|
320
|
-
await this.#handler.unlink(this.#statePath).catch(warn)
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return { mergedDataSize, finalDiskSize }
|
|
324
|
-
}
|
|
325
|
-
}
|
package/disks/RemoteDisk.mjs
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('@xen-orchestra/disk-transform').DiskBlock} DiskBlock
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { RandomAccessDisk } from '@xen-orchestra/disk-transform'
|
|
8
|
-
export class RemoteDisk extends RandomAccessDisk {
|
|
9
|
-
/**
|
|
10
|
-
* Abstract
|
|
11
|
-
* @param {Object} options
|
|
12
|
-
* @param {boolean} options.force
|
|
13
|
-
* @returns {Promise<void>}
|
|
14
|
-
*/
|
|
15
|
-
async init(options = { force: false }) {
|
|
16
|
-
throw new Error(`init must be implemented`)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Abstract
|
|
21
|
-
* @returns {Promise<void>}
|
|
22
|
-
*/
|
|
23
|
-
async close() {
|
|
24
|
-
throw new Error(`close must be implemented`)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Abstract
|
|
29
|
-
* @returns {number}
|
|
30
|
-
*/
|
|
31
|
-
getVirtualSize() {
|
|
32
|
-
throw new Error(`getVirtualSize must be implemented`)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Abstract
|
|
37
|
-
* @returns {number} size
|
|
38
|
-
*/
|
|
39
|
-
getSizeOnDisk() {
|
|
40
|
-
throw new Error(`getSizeOnDisk must be implemented`)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* @returns {number}
|
|
45
|
-
*/
|
|
46
|
-
getBlockSize() {
|
|
47
|
-
throw new Error(`getBlockSize must be implemented`)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Abstract
|
|
52
|
-
* @returns {string}
|
|
53
|
-
*/
|
|
54
|
-
getPath() {
|
|
55
|
-
throw new Error(`getPath must be implemented`)
|
|
56
|
-
}
|
|
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
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Abstract
|
|
70
|
-
* @returns {string}
|
|
71
|
-
*/
|
|
72
|
-
getUuid() {
|
|
73
|
-
throw new Error(`getUuid must be implemented`)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Abstract
|
|
78
|
-
* @returns {Promise<boolean>} canMergeConcurently
|
|
79
|
-
*/
|
|
80
|
-
async canMergeConcurently() {
|
|
81
|
-
throw new Error(`canMergeConcurently must be implemented`)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* @returns {number} getMaxBlockCount
|
|
86
|
-
*/
|
|
87
|
-
getMaxBlockCount() {
|
|
88
|
-
return Math.ceil(this.getVirtualSize() / this.getBlockSize())
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Checks if the VHD contains a specific block.
|
|
93
|
-
* @param {number} index
|
|
94
|
-
* @returns {boolean}
|
|
95
|
-
*/
|
|
96
|
-
hasBlock(index) {
|
|
97
|
-
throw new Error(`hasBlock must be implemented`)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Abstract
|
|
102
|
-
* Gets the indexes of all blocks in the VHD.
|
|
103
|
-
* @returns {Array<number>}
|
|
104
|
-
*/
|
|
105
|
-
getBlockIndexes() {
|
|
106
|
-
throw new Error(`getBlockIndexes must be implemented`)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Abstract
|
|
111
|
-
* Returns the parent non inizialized instance
|
|
112
|
-
* @returns {RemoteDisk}
|
|
113
|
-
*/
|
|
114
|
-
instantiateParent() {
|
|
115
|
-
throw new Error(`instantiateParent must be implemented`)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Abstract
|
|
120
|
-
* Writes a full block.
|
|
121
|
-
* @param {DiskBlock} diskBlock
|
|
122
|
-
* @return {Promise<number>} blockSize
|
|
123
|
-
*/
|
|
124
|
-
async writeBlock(diskBlock) {
|
|
125
|
-
throw new Error(`writeBlock must be implemented`)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Abstract
|
|
130
|
-
* Reads a specific block from the VHD.
|
|
131
|
-
* @param {number} index
|
|
132
|
-
* @returns {Promise<DiskBlock>} diskBlock
|
|
133
|
-
*/
|
|
134
|
-
async readBlock(index) {
|
|
135
|
-
throw new Error(`readBlock must be implemented`)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Abstract
|
|
140
|
-
* Reads a specific block from the child disk to copy/move it to this disk.
|
|
141
|
-
* @param {RemoteDisk} childDisk
|
|
142
|
-
* @param {number} index
|
|
143
|
-
* @param {boolean} isResumingMerge
|
|
144
|
-
* @returns {Promise<number>} blockSize
|
|
145
|
-
*/
|
|
146
|
-
async mergeBlock(childDisk, index, isResumingMerge) {
|
|
147
|
-
throw new Error(`mergeBlock must be implemented`)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Abstract
|
|
152
|
-
* Manually set the disk allocated blocks.
|
|
153
|
-
* @param {Array<number>} blockIds
|
|
154
|
-
* @returns {Promise<void>}
|
|
155
|
-
*/
|
|
156
|
-
async setAllocatedBlocks(blockIds) {
|
|
157
|
-
throw new Error(`setAllocatedBlocks must be implemented`)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Abstract
|
|
162
|
-
* @param {number} blockCount
|
|
163
|
-
* @returns {Promise<void>}
|
|
164
|
-
*/
|
|
165
|
-
async resize(blockCount) {
|
|
166
|
-
throw new Error(`resize must be implemented`)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Abstract
|
|
171
|
-
* @param {RemoteDisk} childDisk
|
|
172
|
-
* @returns {Promise<void>}
|
|
173
|
-
*/
|
|
174
|
-
async flushMetadata(childDisk) {
|
|
175
|
-
throw new Error(`flushMetadata must be implemented`)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Abstract
|
|
180
|
-
* @param {RemoteDisk} childDisk
|
|
181
|
-
* @returns {Promise<void>}
|
|
182
|
-
*/
|
|
183
|
-
mergeMetadata(childDisk) {
|
|
184
|
-
throw new Error(`mergeMetadata must be implemented`)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Abstract
|
|
189
|
-
* Checks if the VHD is a differencing disk.
|
|
190
|
-
* @returns {boolean}
|
|
191
|
-
*/
|
|
192
|
-
isDifferencing() {
|
|
193
|
-
throw new Error(`isDifferencing must be implemented`)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Abstract
|
|
198
|
-
* Rename alias/disk
|
|
199
|
-
* @param {string} newPath
|
|
200
|
-
*/
|
|
201
|
-
async rename(newPath) {
|
|
202
|
-
throw new Error(`rename must be implemented`)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Abstract
|
|
207
|
-
* Deletes alias/disk/disks
|
|
208
|
-
*/
|
|
209
|
-
async unlink() {
|
|
210
|
-
throw new Error(`unlink must be implemented`)
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* @returns {Promise<RemoteDisk>}
|
|
215
|
-
*/
|
|
216
|
-
async openParent() {
|
|
217
|
-
const parent = await super.openParent()
|
|
218
|
-
if (!(parent instanceof RemoteDisk)) {
|
|
219
|
-
throw new Error('parent of a RemoteDisk must be also a RemoteDisk')
|
|
220
|
-
}
|
|
221
|
-
return parent
|
|
222
|
-
}
|
|
223
|
-
}
|