Compare commits
2 Commits
linkSuspen
...
feat_block
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0957b5b6b1 | ||
|
|
33b758d0b2 |
@@ -91,6 +91,7 @@ export default class RemoteHandlerAbstract {
|
||||
const sharedLimit = limitConcurrency(options.maxParallelOperations ?? DEFAULT_MAX_PARALLEL_OPERATIONS)
|
||||
this.closeFile = sharedLimit(this.closeFile)
|
||||
this.copy = sharedLimit(this.copy)
|
||||
this.exists = sharedLimit(this.exists)
|
||||
this.getInfo = sharedLimit(this.getInfo)
|
||||
this.getSize = sharedLimit(this.getSize)
|
||||
this.list = sharedLimit(this.list)
|
||||
@@ -314,6 +315,14 @@ export default class RemoteHandlerAbstract {
|
||||
await this._rmtree(normalizePath(dir))
|
||||
}
|
||||
|
||||
async _exists(file){
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
async exists(file){
|
||||
return this._exists(normalizePath(file))
|
||||
|
||||
}
|
||||
|
||||
// Asks the handler to sync the state of the effective remote with its'
|
||||
// metadata
|
||||
//
|
||||
|
||||
@@ -198,4 +198,9 @@ export default class LocalHandler extends RemoteHandlerAbstract {
|
||||
_writeFile(file, data, { flags }) {
|
||||
return this._addSyncStackTrace(fs.writeFile, this._getFilePath(file), data, { flag: flags })
|
||||
}
|
||||
|
||||
async _exists(file){
|
||||
const exists = await fs.pathExists(this._getFilePath(file))
|
||||
return exists
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,4 +537,17 @@ export default class S3Handler extends RemoteHandlerAbstract {
|
||||
useVhdDirectory() {
|
||||
return true
|
||||
}
|
||||
|
||||
async _exists(file){
|
||||
try{
|
||||
await this._s3.send(new HeadObjectCommand(this._createParams(file)))
|
||||
return true
|
||||
}catch(error){
|
||||
// normalize this error code
|
||||
if (error.name === 'NoSuchKey') {
|
||||
return false
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
> Keep this list alphabetically ordered to avoid merge conflicts
|
||||
|
||||
<!--packages-start-->
|
||||
|
||||
- vhd-lib minor
|
||||
- vhd-cli major
|
||||
- @xen-orchestra/backups-cli major
|
||||
- @xen-orchestra/log minor
|
||||
- xo-cli patch
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
```
|
||||
> vhd-cli
|
||||
Usage:
|
||||
|
||||
vhd-cli check <path>
|
||||
|
||||
Detects issues with VHD. path is relative to the remote url
|
||||
|
||||
Options:
|
||||
--remote <url> the remote url, / if not specified
|
||||
--bat check if the blocks listed in the bat are present on the file system (vhddirectory only)
|
||||
--blocks read all the blocks of the vhd
|
||||
--chain instantiate a vhd with all its parent and check it
|
||||
|
||||
vhd-cli compare <sourceRemoteUrl> <source VHD> <destionationRemoteUrl> <destination>
|
||||
|
||||
Check if two VHD contains the same data
|
||||
|
||||
|
||||
vhd-cli copy <sourceRemoteUrl> <source VHD> <destionationRemoteUrl> <destination> --directory
|
||||
|
||||
Copy a Vhd.
|
||||
|
||||
Options:
|
||||
--directory : the destination vhd will be created as a vhd directory
|
||||
|
||||
vhd-cli info <path>
|
||||
|
||||
Read informations of a VHD, path is relative to /
|
||||
|
||||
vhd-cli merge <child VHD> <parent VHD>
|
||||
|
||||
Merge child in parent, paths are relatives to /
|
||||
|
||||
vhd-cli raw <path>
|
||||
|
||||
extract the raw content of a VHD, path is relative to /
|
||||
|
||||
vhd-cli repl
|
||||
|
||||
create a REPL environnement in the local folder
|
||||
|
||||
|
||||
```
|
||||
|
||||
@@ -1,30 +1,51 @@
|
||||
'use strict'
|
||||
|
||||
const { VhdFile, checkVhdChain } = require('vhd-lib')
|
||||
const { openVhd, VhdSynthetic } = require('vhd-lib')
|
||||
const getopts = require('getopts')
|
||||
const { getSyncedHandler } = require('@xen-orchestra/fs')
|
||||
const { resolve } = require('path')
|
||||
const { Disposable } = require('promise-toolbox')
|
||||
|
||||
const checkVhd = (handler, path) => new VhdFile(handler, path).readHeaderAndFooter()
|
||||
|
||||
module.exports = async function check(rawArgs) {
|
||||
const { chain, _: args } = getopts(rawArgs, {
|
||||
boolean: ['chain'],
|
||||
|
||||
if (args.length < 2 || args.some(_ => _ === '-h' || _ === '--help')) {
|
||||
return `Usage: ${this.command} <path> [--remote <remoteURL>] [--chain] [--bat] [--blocks] `
|
||||
}
|
||||
const { chain, bat, blocks, remote, _: args } = getopts(rawArgs, {
|
||||
boolean: ['chain', 'bat', 'blocks'],
|
||||
default: {
|
||||
chain: false,
|
||||
bat: false,
|
||||
blocks: false
|
||||
},
|
||||
})
|
||||
|
||||
const check = chain ? checkVhdChain : checkVhd
|
||||
await Disposable.use(getSyncedHandler({ url: 'file:///' }), async handler => {
|
||||
for (const vhd of args) {
|
||||
try {
|
||||
await check(handler, resolve(vhd))
|
||||
console.log('ok:', vhd)
|
||||
} catch (error) {
|
||||
console.error('nok:', vhd, error)
|
||||
const vhdPath = args[0]
|
||||
|
||||
await Disposable.factory( async function * open(remote, vhdPath) {
|
||||
const handler = yield getSyncedHandler({url : remote ?? 'file:///'})
|
||||
const vhd = chain? yield VhdSynthetic.fromVhdChain(handler, vhdPath) : yield openVhd(handler, vhdPath)
|
||||
|
||||
await vhd.readBlockAllocationTable()
|
||||
if(bat){
|
||||
const nBlocks = vhd.header.maxTableEntries
|
||||
let nbErrors = 0
|
||||
for (let blockId = 0; blockId < nBlocks; ++blockId) {
|
||||
if(!vhd.containsBlock(blockId)){
|
||||
continue
|
||||
}
|
||||
const ok = await vhd.checkBlock(blockId)
|
||||
if(!ok){
|
||||
console.warn(`block ${blockId} is invalid`)
|
||||
nbErrors ++
|
||||
}
|
||||
}
|
||||
console.log('BAT check done ', nbErrors === 0 ? 'OK': `${nbErrors} block(s) faileds`)
|
||||
}
|
||||
})
|
||||
if(blocks){
|
||||
for await(const _ of vhd.blocks()){
|
||||
|
||||
}
|
||||
console.log('Blocks check done')
|
||||
}
|
||||
})(remote, vhdPath)
|
||||
}
|
||||
|
||||
@@ -394,4 +394,14 @@ exports.VhdAbstract = class VhdAbstract {
|
||||
assert.strictEqual(copied, length, 'invalid length')
|
||||
return copied
|
||||
}
|
||||
|
||||
_checkBlock() {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
||||
// check if a block is ok without reading it
|
||||
// there still can be error when reading the block later (if it's deleted, if right are incorrects,...)
|
||||
async checkBlock(blockId) {
|
||||
return this._checkBlock(blockId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,4 +317,9 @@ exports.VhdDirectory = class VhdDirectory extends VhdAbstract {
|
||||
})
|
||||
this.#compressor = getCompressor(chunkFilters[0])
|
||||
}
|
||||
|
||||
async _checkBlock(blockId){
|
||||
const path = this._getFullBlockPath(blockId)
|
||||
return this._handler.exists(path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,4 +466,8 @@ exports.VhdFile = class VhdFile extends VhdAbstract {
|
||||
async getSize() {
|
||||
return await this._handler.getSize(this._path)
|
||||
}
|
||||
|
||||
_checkBlock(blockId){
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,10 @@ const VhdSynthetic = class VhdSynthetic extends VhdAbstract {
|
||||
return vhd?._getFullBlockPath(blockId)
|
||||
}
|
||||
|
||||
_checkBlock(blockId) {
|
||||
const vhd = this.#getVhdWithBlock(blockId)
|
||||
return vhd?._checkBlock(blockId) ?? false
|
||||
}
|
||||
// return true if all the vhds ar an instance of cls
|
||||
checkVhdsClass(cls) {
|
||||
return this.#vhds.every(vhd => vhd instanceof cls)
|
||||
|
||||
Reference in New Issue
Block a user