feat(vhd-cli compare): compare metadata and content of two VHDs (#5920)
This commit is contained in:
parent
7ef89d5043
commit
c62d727cbe
81
packages/vhd-cli/src/commands/compare.js
Normal file
81
packages/vhd-cli/src/commands/compare.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { getSyncedHandler } from '@xen-orchestra/fs'
|
||||||
|
import { openVhd, Constants } from 'vhd-lib'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
import Disposable from 'promise-toolbox/Disposable'
|
||||||
|
import omit from 'lodash/omit'
|
||||||
|
|
||||||
|
const deepCompareObjects = function (src, dest, path) {
|
||||||
|
for (const key of Object.keys(src)) {
|
||||||
|
const srcValue = src[key]
|
||||||
|
const destValue = dest[key]
|
||||||
|
if (srcValue !== destValue) {
|
||||||
|
const srcType = typeof srcValue
|
||||||
|
const destType = typeof destValue
|
||||||
|
if (srcType !== destType) {
|
||||||
|
throw new Error(`key ${path + '/' + key} is of type *${srcType}* in source and *${destType}* in dest`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srcType !== 'object') {
|
||||||
|
throw new Error(`key ${path + '/' + key} is *${srcValue}* in source and *${destValue}* in dest`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Buffer.isBuffer(srcValue)) {
|
||||||
|
if (!(Buffer.isBuffer(destValue) && srcValue.equals(destValue))) {
|
||||||
|
throw new Error(`key ${path + '/' + key} is buffer in source that does not equal dest`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deepCompareObjects(src[key], dest[key], path + '/' + key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async args => {
|
||||||
|
if (args.length < 2 || args.some(_ => _ === '-h' || _ === '--help')) {
|
||||||
|
return `Usage: compare <source VHD> <destination> `
|
||||||
|
}
|
||||||
|
const [sourcePath, destPath] = args
|
||||||
|
|
||||||
|
await Disposable.use(async function* () {
|
||||||
|
const handler = yield getSyncedHandler({ url: 'file:///' })
|
||||||
|
const src = yield openVhd(handler, resolve(sourcePath))
|
||||||
|
const dest = yield openVhd(handler, resolve(destPath))
|
||||||
|
|
||||||
|
// parent locator entries contains offset that can be different without impacting the vhd
|
||||||
|
// we'll compare them later
|
||||||
|
// table offset and checksum are also implementation specific
|
||||||
|
const ignoredEntries = ['checksum', 'parentLocatorEntry', 'tableOffset']
|
||||||
|
deepCompareObjects(omit(src.header, ignoredEntries), omit(dest.header, ignoredEntries), 'header')
|
||||||
|
deepCompareObjects(src.footer, dest.footer, 'footer')
|
||||||
|
|
||||||
|
await src.readBlockAllocationTable()
|
||||||
|
await dest.readBlockAllocationTable()
|
||||||
|
|
||||||
|
for (let i = 0; i < src.header.maxTableEntries; i++) {
|
||||||
|
if (src.containsBlock(i)) {
|
||||||
|
if (dest.containsBlock(i)) {
|
||||||
|
const srcBlock = await src.readBlock(i)
|
||||||
|
const destBlock = await dest.readBlock(i)
|
||||||
|
|
||||||
|
if (!srcBlock.buffer.equals(destBlock.buffer)) {
|
||||||
|
throw new Error(`Block ${i} has different data in src and dest`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Block ${i} is present in source but not in dest `)
|
||||||
|
}
|
||||||
|
} else if (dest.containsBlock(i)) {
|
||||||
|
throw new Error(`Block ${i} is present in dest but not in source `)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let parentLocatorId = 0; parentLocatorId < Constants.PARENT_LOCATOR_ENTRIES; parentLocatorId++) {
|
||||||
|
const srcParentLocator = await src.readParentLocator(parentLocatorId)
|
||||||
|
const destParentLocator = await dest.readParentLocator(parentLocatorId)
|
||||||
|
if (!srcParentLocator.data || !srcParentLocator.data.equals(destParentLocator.data)) {
|
||||||
|
console.log(srcParentLocator, destParentLocator)
|
||||||
|
throw new Error(`Parent Locator ${parentLocatorId} has different data in src and dest`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('there is no difference between theses vhd')
|
||||||
|
})
|
||||||
|
}
|
@ -54,7 +54,7 @@ export class VhdFile extends VhdAbstract {
|
|||||||
#uncheckedBlockTable
|
#uncheckedBlockTable
|
||||||
|
|
||||||
get #blocktable() {
|
get #blocktable() {
|
||||||
assert.notStrictEqual(this.#blockTable, undefined, 'Block table must be initialized before access')
|
assert.notStrictEqual(this.#uncheckedBlockTable, undefined, 'Block table must be initialized before access')
|
||||||
return this.#uncheckedBlockTable
|
return this.#uncheckedBlockTable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,5 +8,7 @@ export { default as createSyntheticStream } from './createSyntheticStream'
|
|||||||
export { default as createVhdStreamWithLength } from './createVhdStreamWithLength'
|
export { default as createVhdStreamWithLength } from './createVhdStreamWithLength'
|
||||||
export { default as mergeVhd } from './merge'
|
export { default as mergeVhd } from './merge'
|
||||||
export { default as peekFooterFromVhdStream } from './peekFooterFromVhdStream'
|
export { default as peekFooterFromVhdStream } from './peekFooterFromVhdStream'
|
||||||
|
export { openVhd } from './openVhd'
|
||||||
export { VhdDirectory } from './Vhd/VhdDirectory'
|
export { VhdDirectory } from './Vhd/VhdDirectory'
|
||||||
export { VhdFile } from './Vhd/VhdFile'
|
export { VhdFile } from './Vhd/VhdFile'
|
||||||
|
export * as Constants from './_constants'
|
||||||
|
12
packages/vhd-lib/src/openVhd.js
Normal file
12
packages/vhd-lib/src/openVhd.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { VhdFile, VhdDirectory } from './'
|
||||||
|
|
||||||
|
export async function openVhd(handler, path) {
|
||||||
|
try {
|
||||||
|
return await VhdFile.open(handler, path)
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code !== 'EISDIR') {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
return await VhdDirectory.open(handler, path)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user