This commit is contained in:
Florent Beauchamp
2022-12-16 13:52:55 +01:00
parent 19b0d5f584
commit 0876de77f5
4 changed files with 48 additions and 117 deletions

View File

@@ -4,7 +4,7 @@ import { strictEqual } from 'node:assert'
import fetch from 'node-fetch';
import parseVmx from './parsers/vmx.mjs'
import { basename, dirname } from 'node:path'
import { dirname } from 'node:path'
import parseVmdk, { VhdCowd } from './parsers/vmdk.mjs'
import parseVmsd from './parsers/vmsd.mjs';
import VhdEsxiRaw from './VhdEsxiRaw.mjs';
@@ -173,29 +173,36 @@ export default class Esxi extends EventEmitter {
descriptionLabel: ' from esxi',
vhd: async () => {
console.log('vhd from snapshot')
if(fileName.endsWith('-flat.vmdk')){
return
console.log('snapshot flat.vmdk vhd ')
const vhd = await VhdEsxiRaw.open(this,diskDataStore, dirname(diskPath) + '/' + fileName)
console.log('snapshot flat.vmdk vhd openned')
await vhd.readBlockAllocationTable()
console.log('snapshot flat.vmdk bat openned')
return vhd.stream()
}
console.log('vhd from snapshot filename ok')
console.log('snapshot current.vmdk filenameok')
// last snasphot only works when vm is powered off
const vhd = await VhdCowd.open(this, diskDataStore, dirname(diskPath) + '/' + fileName, parentFileName)
console.log('realy vhd from snapshot openned')
console.log('snapshot current.vmdk vhd openned')
await vhd.readBlockAllocationTable()
console.log('realy vhd from snapshot bat ok ')
console.log('snapshot current.vmdk bat openned')
return vhd.stream()
},
rawStream: async () => {
console.log('rawStream from snapshot')
console.log('snapshot rawStream from snapshot')
if(!fileName.endsWith('-flat.vmdk')){
return
}
console.log('flat.vmdk ')
const vhd = await VhdEsxiRaw.open(this, diskDataStore, dirname(diskPath) + '/' + fileName, parentFileName)
console.log('flat.vmdk vhd openned')
await vhd.readBlockAllocationTable()
console.log('flat.vmdk bat openned')
return vhd.stream()
console.log('snapshot rawStream from snapshot filename ok')
// @todo : only if vm is powered off
const stream = (await this.download(diskDataStore, dirname(diskPath) + '/' + fileName)).body
console.log('snapshot realy vhd from snapshot got stream ')
stream.length = capacity
return stream
},
}
}
@@ -276,7 +283,12 @@ export default class Esxi extends EventEmitter {
vhd: async () => {
console.log('current.vmdk')
if(fileName.endsWith('-flat.vmdk')){
return
console.log('flat.vmdk vhd ')
const vhd = await VhdEsxiRaw.open(this, datastore, path + '/' + fileName)
console.log('flat.vmdk vhd openned')
await vhd.readBlockAllocationTable()
console.log('flat.vmdk bat openned')
return vhd.stream()
}
console.log('current.vmdk filenameok')
// last snasphot only works when vm is powered off
@@ -291,12 +303,14 @@ export default class Esxi extends EventEmitter {
if(fileName.endsWith('-flat.vmdk')){
return
}
// @todo : only if vm is powered off or with snapshot
const vhd = await VhdEsxiRaw.open(this, datastore, path + '/' + fileName, parentFileName)
console.log('flat.vmdk vhd openned')
await vhd.readBlockAllocationTable()
console.log('flat.vmdk bat openned')
return vhd.stream()
console.log('rawStream from snapshot filename ok')
// @todo : only if vm is powered off
const stream = (await this.download(datastore, path + '/' + fileName)).body
console.log('realy vhd from snapshot got stream ')
stream.length = other.capacity
return stream
},
}
}),

View File

@@ -23,11 +23,8 @@ export class VhdCowd extends VhdAbstract{
#grainDirectory
static async open(esxi, datastore, path) {
console.log('vmdcowd.open ', datastore, path)
const vhd = new VhdCowd(esxi, datastore, path)
console.log('vmdcowd.open instantiated ')
await vhd.readHeaderAndFooter()
console.log('vmdcowd.open readHeaderAndFooter ')
return vhd
}
constructor(esxi, datastore, path, parentFileName){
@@ -85,7 +82,6 @@ export class VhdCowd extends VhdAbstract{
this.#header.parentUnicodeName = this.#parentFileName
const geometry = _computeGeometryForSize(size)
const actualSize = geometry.actualSize
console.log(' DELTA ? ',this.#parentFileName ? 'yes' : 'no')
this.#footer = unpackFooter(createFooter(actualSize, Math.floor(Date.now() / 1000), geometry, FOOTER_SIZE, this.#parentFileName ? DISK_TYPES.DIFFERENCING : DISK_TYPES.DYNAMIC))
}
@@ -140,7 +136,6 @@ export class VhdCowd extends VhdAbstract{
const OVERPROVISION = 3
for(let i=1; i < offsets.length; i ++){
if(offsets[i-1] + OVERPROVISION < offsets[i]){
// console.log('non contiguous', startOffset, offsets[i-1], offsets[i] )
ranges.push({startOffset, endOffset: offsets[i-1]})
startOffset = offsets[i]
}
@@ -152,13 +147,10 @@ export class VhdCowd extends VhdAbstract{
const startIndex = fileOffsetToIndexInGrainTable[startOffset]
const startInBlock = startIndex * 512 + 512 /* block bitmap */
const sectors = await this.#read(startOffset*512,endOffset*512 - 1)
console.log({startOffset, endOffset, sectors})
// @todo : if overprovision > 1 , it may copy random data from the vmdk
sectors.copy(buf,startInBlock)
// console.log('got sectors and copied',sectors.length,' to ',startInBlock)
}
//console.log({blockId, buf})
return {
id: blockId,
bitmap: buf.slice(0, 512),

View File

@@ -1300,9 +1300,9 @@ export { import_ as import }
export async function importFomEsxi({host, user, password, sslVerify, sr, network}){
//console.log({host, user, password, sslVerify, sr, network})
await this.migrationfromEsxi({host, user, password, sslVerify, sr, network})
export async function importFomEsxi({host, user, password, sslVerify=true, sr, network, vm, thin=false}){
console.log({host, user, password, sslVerify, sr, network, vm, thin})
await this.migrationfromEsxi({host, user, password, sslVerify, thin, vm, sr, network})
return
}
@@ -1312,7 +1312,9 @@ importFomEsxi.params = {
password: { type: 'string' },
user: { type: 'string' },
sr: { type: 'string' },
sslVerify: {type: 'boolean'}
sslVerify: {type: 'boolean', optional: true},
vm:{type: 'string'},
thin:{type: 'boolean', optional: true}
}

View File

@@ -5,30 +5,6 @@ import OTHER_CONFIG_TEMPLATE from '../xapi/other-config-template.mjs'
import asyncMapSettled from '@xen-orchestra/async-map/legacy.js'
import { fromEvent } from 'promise-toolbox'
import { VDI_FORMAT_RAW, VDI_FORMAT_VHD } from '@xen-orchestra/xapi'
import { readChunk } from '@vates/read-chunk'
async function checkEmpty(stream){
let chunk
let nbEmpty = 0
let nbFull = 0
const empty = Buffer.alloc(2*1024*1024, 0)
while(chunk = await readChunk(stream, 2 * 1024 * 1024)){
const isEmpty = empty.equals(chunk)
if(isEmpty){
nbEmpty ++
} else {
nbFull ++
}
if((nbEmpty + nbFull)%10 ===0){
console.log('.',{nbEmpty, nbFull})
}
}
console.log({nbEmpty, nbFull})
}
export default class MigrateVm {
constructor(app) {
@@ -136,22 +112,8 @@ export default class MigrateVm {
}
}
async #coldMigrationFromEsxi({ memory, numCpu, name_label, disks, networks, powerState, ...other }, srId, networkId){
console.log('metadata', { memory, numCpu, name_label, disks, networks, other })
if (powerState !== 'powerOff') {
console.log('RUNNING ', powerState)
}
const sr = this._app.getXapiObject(srId)
const xapi = sr.$xapi
// find correspondig networks
console.log('disk and network created')
return vmChooseCoresPerSocket
}
async migrationfromEsxi({ host, user, password, sslVerify, sr: srId, network: networkId }) {
async migrationfromEsxi({ host, user, password, sslVerify, sr: srId, network: networkId, vm:vmId ,thin}) {
const esxi = new Esxi(host, user, password, sslVerify)
const app = this._app
const sr = app.getXapiObject(srId)
@@ -161,7 +123,7 @@ export default class MigrateVm {
await fromEvent(esxi, 'ready')
console.log('connected')
const esxiVmMetadata = await esxi.getTransferableVmMetadata(
'9'
vmId
)
const { memory,name_label, networks, numCpu } = esxiVmMetadata
const vm = await xapi._getOrWaitObject(
@@ -185,7 +147,6 @@ export default class MigrateVm {
])
const vifDevices = await xapi.call('VM.get_allowed_VIF_devices', vm.$ref)
console.log({ vifDevices })
await Promise.all(networks.map((network, i) =>
xapi.VIF_create({
@@ -195,40 +156,6 @@ export default class MigrateVm {
})
))
console.log('network created')
/*
await Promise.all(
disks
.map(async disk => {
const vdi = await xapi._getOrWaitObject(
await xapi.VDI_create({
name_description: disk.descriptionLabel,
name_label: disk.nameLabel,
SR: sr.$ref,
virtual_size: disk.capacity,
})
)
await xapi.VBD_create({
userdevice: String(disk.position),
VDI: vdi.$ref,
VM: vm.$ref,
})
console.log('will import disk', (await disk.rawStream()).length)
console.log('')
let format = VDI_FORMAT_VHD
let stream = await disk.vhd()
if(!stream){
format = VDI_FORMAT_RAW
stream = await disk.rawStream()
}
await vdi.$importContent(stream, { format })
console.log('disk imported')
})
)
*/
// CREATE VM
// get the snapshot to migrate
const snapshots = esxiVmMetadata.snapshots
@@ -239,8 +166,7 @@ export default class MigrateVm {
const chain = [currentSnapshot.disks]
while(currentSnapshot = snapshots.snapshots.find(({uid})=> uid === currentSnapshot.parent)){
console.log('found parent ', currentSnapshot)
chain.push(currentSnapshot.disks)
// chain.push(currentSnapshot.disks)
}
chain.reverse()
chain.push( esxiVmMetadata.disks)
@@ -253,9 +179,7 @@ export default class MigrateVm {
})
})
console.log(chainsByNodes)
for(const node in chainsByNodes){
console.log('IMPORTING chain' , node)
let chainByNode = chainsByNodes[node]
const vdi = await xapi._getOrWaitObject(
@@ -266,14 +190,12 @@ export default class MigrateVm {
virtual_size: chainByNode[0].capacity,
})
)
console.log('vdi created')
await xapi.VBD_create({
userdevice: String(0),
VDI: vdi.$ref,
VM: vm.$ref,
})
console.log('vbd created')
for(const disk of chainByNode){
// the first one is a RAW disk ( full )
@@ -282,16 +204,17 @@ export default class MigrateVm {
console.log('will import ',{ disk})
let format = VDI_FORMAT_VHD
let stream = await disk.vhd()
if(!stream){
// format = VDI_FORMAT_RAW
let stream
if(!thin){
stream = await disk.rawStream()
format = VDI_FORMAT_RAW
}
if(!stream){
stream = await disk.vhd()
}
console.log('will import in format ',{format})
//await checkEmpty(stream)
await vdi.$importContent(stream, { format })
console.log('disk imported')
break;
}
// and then we can import the running disk ( after shutting down the VM)