Files
xen-orchestra/packages/xo-vmdk-to-vhd/src/ova.integ.js
2023-05-16 21:01:03 +02:00

244 lines
11 KiB
JavaScript

import { afterEach, beforeEach, test } from 'test'
import { strict as assert } from 'assert'
import { exec } from 'child-process-promise'
import { createReadStream } from 'fs'
import { stat, writeFile } from 'fs-extra'
import getStream from 'get-stream'
import { pFromCallback } from 'promise-toolbox'
import rimraf from 'rimraf'
import tmp from 'tmp'
import { ParsableFile, parseOVAFile } from './ova-read'
import readVmdkGrainTable from './vmdk-read-table'
const initialDir = process.cwd()
beforeEach(async () => {
const dir = await pFromCallback(cb => tmp.dir(cb))
process.chdir(dir)
})
afterEach(async () => {
const tmpDir = process.cwd()
process.chdir(initialDir)
await rimraf(tmpDir)
})
export class NodeParsableFile extends ParsableFile {
constructor(fileName, fileLength = Infinity) {
super()
this._fileName = fileName
this._start = 0
this._end = fileLength
}
slice(start, end) {
const newFile = new NodeParsableFile(this._fileName)
newFile._start = start < 0 ? this._end + start : this._start + start
newFile._end = end < 0 ? this._end + end : this._start + end
return newFile
}
async read() {
const result = await getStream.buffer(
createReadStream(this._fileName, {
start: this._start,
end: this._end - 1,
})
)
// crazy stuff to get a browser-compatible ArrayBuffer from a node buffer
// https://stackoverflow.com/a/31394257/72637
return result.buffer.slice(result.byteOffset, result.byteOffset + result.byteLength)
}
}
const vmdkFileName = 'random-data.vmdk'
test('An ova file is parsed correctly', async () => {
const ovfName = 'test.ovf'
await writeFile(ovfName, xmlContent)
const rawFileName = 'random-data'
await exec(`base64 /dev/urandom | head -c 104448 > ${rawFileName}`)
await exec(
`rm -f ${vmdkFileName} && python /usr/lib/python3/dist-packages/VMDKstream.py ${rawFileName} ${vmdkFileName}`
)
const ovaName = `test.ova`
await exec(`tar cf ${ovaName} ${ovfName} ${vmdkFileName}`)
const vmdkParsableFile = new NodeParsableFile(vmdkFileName, (await stat(vmdkFileName)).size)
const directGrainTableFetch = await readVmdkGrainTable(async (start, end) =>
vmdkParsableFile.slice(start, end).read()
)
assert.deepEqual(directGrainTableFetch, expectedResult.tables[vmdkFileName])
const data = await parseOVAFile(new NodeParsableFile(ovaName), (buffer, encoder) => {
return Buffer.from(buffer).toString(encoder)
})
for (const fileName in data.tables) {
data.tables[fileName] = await data.tables[fileName]
}
assert.deepEqual(data, expectedResult)
})
function arrayToBuffer(array) {
const output = new DataView(new ArrayBuffer(array.length * 4))
array.forEach((e, i) => {
output.setUint32(i * 4, e, true)
})
return output.buffer
}
const expectedResult = {
tables: {
[vmdkFileName]: {
grainFileOffsetList: arrayToBuffer([65536, 115712]),
grainLogicalAddressList: arrayToBuffer([0, 65536]),
},
},
disks: {
vmdisk1: {
capacity: 134217728,
path: 'random-data.vmdk',
descriptionLabel: 'No description',
nameLabel: 'Hard Disk 1',
position: 0,
},
},
networks: ['LAN'],
nameLabel: 'dsl',
descriptionLabel: "NetworkJutsu's Damn Small Linux OVA",
nCpus: 1,
memory: 67108864,
}
const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
<!--Generated by VMware ESX Server, User: root, UTC time: 2015-09-20T04:41:19.332937Z-->
<Envelope vmw:buildId="build-2615704" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<References>
<File ovf:href="${vmdkFileName}" ovf:id="file1" ovf:size="76934656" />
</References>
<DiskSection>
<Info>Virtual disk information</Info>
<Disk ovf:capacity="128" ovf:capacityAllocationUnits="byte * 2^20" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="82313216" />
</DiskSection>
<NetworkSection>
<Info>The list of logical networks</Info>
<Network ovf:name="LAN">
<Description>The LAN network</Description>
</Network>
</NetworkSection>
<VirtualSystem ovf:id="dsl">
<Info>A virtual machine</Info>
<Name>dsl</Name>
<OperatingSystemSection ovf:id="1" vmw:osType="otherGuest">
<Info>The kind of installed guest operating system</Info>
</OperatingSystemSection>
<VirtualHardwareSection>
<Info>Virtual hardware requirements</Info>
<System>
<vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
<vssd:InstanceID>0</vssd:InstanceID>
<vssd:VirtualSystemIdentifier>dsl</vssd:VirtualSystemIdentifier>
<vssd:VirtualSystemType>vmx-11</vssd:VirtualSystemType>
</System>
<Item>
<rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
<rasd:Description>Number of Virtual CPUs</rasd:Description>
<rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
<rasd:InstanceID>1</rasd:InstanceID>
<rasd:ResourceType>3</rasd:ResourceType>
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
</Item>
<Item>
<rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
<rasd:Description>Memory Size</rasd:Description>
<rasd:ElementName>64MB of memory</rasd:ElementName>
<rasd:InstanceID>2</rasd:InstanceID>
<rasd:ResourceType>4</rasd:ResourceType>
<rasd:VirtualQuantity>64</rasd:VirtualQuantity>
</Item>
<Item>
<rasd:Address>1</rasd:Address>
<rasd:Description>IDE Controller</rasd:Description>
<rasd:ElementName>VirtualIDEController 1</rasd:ElementName>
<rasd:InstanceID>3</rasd:InstanceID>
<rasd:ResourceType>5</rasd:ResourceType>
</Item>
<Item>
<rasd:Address>0</rasd:Address>
<rasd:Description>IDE Controller</rasd:Description>
<rasd:ElementName>VirtualIDEController 0</rasd:ElementName>
<rasd:InstanceID>4</rasd:InstanceID>
<rasd:ResourceType>5</rasd:ResourceType>
</Item>
<Item ovf:required="false">
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
<rasd:ElementName>VirtualVideoCard</rasd:ElementName>
<rasd:InstanceID>5</rasd:InstanceID>
<rasd:ResourceType>24</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="enableMPTSupport" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="use3dRenderer" vmw:value="automatic" />
<vmw:Config ovf:required="false" vmw:key="useAutoDetect" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="videoRamSizeInKB" vmw:value="4096" />
</Item>
<Item ovf:required="false">
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
<rasd:ElementName>VirtualVMCIDevice</rasd:ElementName>
<rasd:InstanceID>6</rasd:InstanceID>
<rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType>
<rasd:ResourceType>1</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="allowUnrestrictedCommunication" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="33" />
</Item>
<Item ovf:required="false">
<rasd:AddressOnParent>0</rasd:AddressOnParent>
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
<rasd:ElementName>CD-ROM 1</rasd:ElementName>
<rasd:InstanceID>7</rasd:InstanceID>
<rasd:Parent>3</rasd:Parent>
<rasd:ResourceSubType>vmware.cdrom.remoteatapi</rasd:ResourceSubType>
<rasd:ResourceType>15</rasd:ResourceType>
</Item>
<Item>
<rasd:AddressOnParent>0</rasd:AddressOnParent>
<rasd:ElementName>Hard Disk 1</rasd:ElementName>
<rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
<rasd:InstanceID>8</rasd:InstanceID>
<rasd:Parent>4</rasd:Parent>
<rasd:ResourceType>17</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="backing.writeThrough" vmw:value="false" />
</Item>
<Item>
<rasd:AddressOnParent>7</rasd:AddressOnParent>
<rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
<rasd:Connection>LAN</rasd:Connection>
<rasd:Description>PCNet32 ethernet adapter on "LAN"</rasd:Description>
<rasd:ElementName>Ethernet 1</rasd:ElementName>
<rasd:InstanceID>9</rasd:InstanceID>
<rasd:ResourceSubType>PCNet32</rasd:ResourceSubType>
<rasd:ResourceType>10</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="32" />
<vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="true" />
</Item>
<vmw:Config ovf:required="false" vmw:key="cpuHotAddEnabled" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="cpuHotRemoveEnabled" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="bios" />
<vmw:Config ovf:required="false" vmw:key="virtualICH7MPresent" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="virtualSMCPresent" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="nestedHVEnabled" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="soft" />
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft" />
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.standbyAction" vmw:value="powerOnSuspend" />
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="hard" />
<vmw:Config ovf:required="false" vmw:key="tools.afterPowerOn" vmw:value="true" />
<vmw:Config ovf:required="false" vmw:key="tools.afterResume" vmw:value="true" />
<vmw:Config ovf:required="false" vmw:key="tools.beforeGuestShutdown" vmw:value="true" />
<vmw:Config ovf:required="false" vmw:key="tools.beforeGuestStandby" vmw:value="true" />
<vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false" />
<vmw:Config ovf:required="false" vmw:key="tools.toolsUpgradePolicy" vmw:value="manual" />
</VirtualHardwareSection>
<AnnotationSection ovf:required="false">
<Info>A human-readable annotation</Info>
<Annotation>NetworkJutsu's Damn Small Linux OVA</Annotation>
</AnnotationSection>
</VirtualSystem>
</Envelope>`