Compare commits
2 Commits
fix_stats_
...
feat_block
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0957b5b6b1 | ||
|
|
33b758d0b2 |
@@ -91,6 +91,7 @@ export default class RemoteHandlerAbstract {
|
|||||||
const sharedLimit = limitConcurrency(options.maxParallelOperations ?? DEFAULT_MAX_PARALLEL_OPERATIONS)
|
const sharedLimit = limitConcurrency(options.maxParallelOperations ?? DEFAULT_MAX_PARALLEL_OPERATIONS)
|
||||||
this.closeFile = sharedLimit(this.closeFile)
|
this.closeFile = sharedLimit(this.closeFile)
|
||||||
this.copy = sharedLimit(this.copy)
|
this.copy = sharedLimit(this.copy)
|
||||||
|
this.exists = sharedLimit(this.exists)
|
||||||
this.getInfo = sharedLimit(this.getInfo)
|
this.getInfo = sharedLimit(this.getInfo)
|
||||||
this.getSize = sharedLimit(this.getSize)
|
this.getSize = sharedLimit(this.getSize)
|
||||||
this.list = sharedLimit(this.list)
|
this.list = sharedLimit(this.list)
|
||||||
@@ -314,6 +315,14 @@ export default class RemoteHandlerAbstract {
|
|||||||
await this._rmtree(normalizePath(dir))
|
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'
|
// Asks the handler to sync the state of the effective remote with its'
|
||||||
// metadata
|
// metadata
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -198,4 +198,9 @@ export default class LocalHandler extends RemoteHandlerAbstract {
|
|||||||
_writeFile(file, data, { flags }) {
|
_writeFile(file, data, { flags }) {
|
||||||
return this._addSyncStackTrace(fs.writeFile, this._getFilePath(file), data, { flag: 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() {
|
useVhdDirectory() {
|
||||||
return true
|
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
|
> Keep this list alphabetically ordered to avoid merge conflicts
|
||||||
|
|
||||||
<!--packages-start-->
|
<!--packages-start-->
|
||||||
|
- vhd-lib minor
|
||||||
|
- vhd-cli major
|
||||||
- @xen-orchestra/backups-cli major
|
- @xen-orchestra/backups-cli major
|
||||||
- @xen-orchestra/log minor
|
- @xen-orchestra/log minor
|
||||||
- xo-cli patch
|
- 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'
|
'use strict'
|
||||||
|
|
||||||
const { VhdFile, checkVhdChain } = require('vhd-lib')
|
const { openVhd, VhdSynthetic } = require('vhd-lib')
|
||||||
const getopts = require('getopts')
|
const getopts = require('getopts')
|
||||||
const { getSyncedHandler } = require('@xen-orchestra/fs')
|
const { getSyncedHandler } = require('@xen-orchestra/fs')
|
||||||
const { resolve } = require('path')
|
|
||||||
const { Disposable } = require('promise-toolbox')
|
const { Disposable } = require('promise-toolbox')
|
||||||
|
|
||||||
const checkVhd = (handler, path) => new VhdFile(handler, path).readHeaderAndFooter()
|
|
||||||
|
|
||||||
module.exports = async function check(rawArgs) {
|
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: {
|
default: {
|
||||||
chain: false,
|
chain: false,
|
||||||
|
bat: false,
|
||||||
|
blocks: false
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const check = chain ? checkVhdChain : checkVhd
|
const vhdPath = args[0]
|
||||||
await Disposable.use(getSyncedHandler({ url: 'file:///' }), async handler => {
|
|
||||||
for (const vhd of args) {
|
await Disposable.factory( async function * open(remote, vhdPath) {
|
||||||
try {
|
const handler = yield getSyncedHandler({url : remote ?? 'file:///'})
|
||||||
await check(handler, resolve(vhd))
|
const vhd = chain? yield VhdSynthetic.fromVhdChain(handler, vhdPath) : yield openVhd(handler, vhdPath)
|
||||||
console.log('ok:', vhd)
|
|
||||||
} catch (error) {
|
await vhd.readBlockAllocationTable()
|
||||||
console.error('nok:', vhd, error)
|
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')
|
assert.strictEqual(copied, length, 'invalid length')
|
||||||
return copied
|
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])
|
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() {
|
async getSize() {
|
||||||
return await this._handler.getSize(this._path)
|
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)
|
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
|
// return true if all the vhds ar an instance of cls
|
||||||
checkVhdsClass(cls) {
|
checkVhdsClass(cls) {
|
||||||
return this.#vhds.every(vhd => vhd instanceof cls)
|
return this.#vhds.every(vhd => vhd instanceof cls)
|
||||||
|
|||||||
Reference in New Issue
Block a user