@vmngr/libvirt
Version:
Libvirt bindings for Node.js®
421 lines (341 loc) • 13 kB
text/typescript
/**
* Copyright 2019 Leon Rinkel <leon@rinkel.me> and vmngr/libvirt contributers.
*
* This file is part of the vmngr/libvirt project and is subject to the MIT
* license as in the LICENSE file in the project root.
*
* @brief Contains helper functions to deserialize / serialize domain
* descriptions from / to XML.
*/
import xml2js from "xml2js";
import {
DomainDesc,
DomainOsDesc,
DomainDiskDesc,
DomainInterfaceDesc,
DomainGraphicsDesc,
} from "./domain-desc";
export const domainOsXml = {
serialize(osDesc: DomainOsDesc): { } {
// tslint:disable-next-line:no-any
const os: any = { $: { } };
if (osDesc.type) {
os.type = { $: { } };
if (osDesc.type.arch) os.type.$.arch = osDesc.type.arch;
if (osDesc.type.machine) os.type.$.machine = osDesc.type.machine;
if (osDesc.type.value) os.type._ = osDesc.type.value;
}
if (osDesc.boot) {
os.boot = { $: { } };
if (osDesc.boot.dev) os.boot.$.dev = osDesc.boot.dev;
}
return os;
},
// tslint:disable-next-line:no-any
deserialize(os: any): DomainOsDesc {
const osDesc: DomainOsDesc = { };
if (os.type[0]) {
osDesc.type = { };
if (os.type[0].$.arch) {
osDesc.type.arch = os.type[0].$.arch;
}
if (os.type[0].$.machine) {
osDesc.type.machine = os.type[0].$.machine;
}
if (os.type[0]._) osDesc.type.value = os.type[0]._;
}
if (os.boot[0]) {
osDesc.boot = { };
if (os.boot[0].$.dev) osDesc.boot.dev = os.boot[0].$.dev;
}
return osDesc;
},
};
export const domainDiskXml = {
serialize(diskDesc: DomainDiskDesc): { } {
// tslint:disable-next-line:no-any
const disk: any = { $: { } };
if (diskDesc.type) disk.$.type = diskDesc.type;
if (diskDesc.device) disk.$.device = diskDesc.device;
if (diskDesc.driver) {
disk.driver = { $: { } };
if (diskDesc.driver.name) disk.driver.$.name = diskDesc.driver.name;
if (diskDesc.driver.type) disk.driver.$.type = diskDesc.driver.type;
}
if (diskDesc.source) {
disk.source = { $: { } };
if (diskDesc.source.file) disk.source.$.file = diskDesc.source.file;
}
if (diskDesc.target) {
disk.target = { $: { } };
if (diskDesc.target.dev) disk.target.$.dev = diskDesc.target.dev;
if (diskDesc.target.bus) disk.target.$.bus = diskDesc.target.bus;
}
return disk;
},
// tslint:disable-next-line:no-any
deserialize(disk: any): DomainDiskDesc {
const diskDesc: DomainDiskDesc = { };
if (disk.$.type) diskDesc.type = disk.$.type;
if (disk.$.device) diskDesc.device = disk.$.device;
if (disk.driver[0]) {
diskDesc.driver = { };
if (disk.driver[0].$.name) {
diskDesc.driver.name = disk.driver[0].$.name;
}
if (disk.driver[0].$.type) {
diskDesc.driver.type = disk.driver[0].$.type;
}
}
if (disk.source[0]) {
diskDesc.source = { };
if (disk.source[0].$.file) {
diskDesc.source.file = disk.source[0].$.file;
}
}
if (disk.target[0]) {
diskDesc.target = { };
if (disk.target[0].$.dev) {
diskDesc.target.dev = disk.target[0].$.dev;
}
if (disk.target[0].$.bus) {
diskDesc.target.bus = disk.target[0].$.bus;
}
}
return diskDesc;
},
};
export const domainInterfaceXml = {
serialize(interfaceDesc: DomainInterfaceDesc): { } {
// tslint:disable-next-line:no-any
const iface: any = { $: { } };
if (interfaceDesc.type) iface.$.type = interfaceDesc.type;
if (interfaceDesc.source) {
iface.source = { $: { } };
if (interfaceDesc.source.network) {
iface.source.$.network = interfaceDesc.source.network;
}
}
if (interfaceDesc.mac) {
iface.mac = { $: { } };
if (interfaceDesc.mac.address) {
iface.mac.$.address = interfaceDesc.mac.address;
}
}
if (interfaceDesc.model) {
iface.model = { $: { } };
if (interfaceDesc.model.type) {
iface.model.$.type = interfaceDesc.model.type;
}
}
return iface;
},
// tslint:disable-next-line:no-any
deserialize(iface: any): DomainInterfaceDesc {
const interfaceDesc: DomainInterfaceDesc = { };
if (iface.$.type) interfaceDesc.type = iface.$.type;
if (iface.source[0]) {
interfaceDesc.source = { };
if (iface.source[0].$.network) {
interfaceDesc.source.network = iface.source[0].$.network;
}
}
if (iface.mac[0]) {
interfaceDesc.mac = { };
if (iface.mac[0].$.address) {
interfaceDesc.mac.address = iface.mac[0].$.address;
}
}
if (iface.model[0]) {
interfaceDesc.model = { };
if (iface.model[0].$.type) {
interfaceDesc.model.type = iface.model[0].$.type;
}
}
return interfaceDesc;
},
};
export const domainGraphicsXml = {
serialize(graphicsDesc: DomainGraphicsDesc): { } {
// tslint:disable-next-line:no-any
const graphics: any = { $: { } };
if (graphicsDesc.type) graphics.$.type = graphicsDesc.type;
if (graphicsDesc.port) graphics.$.port = graphicsDesc.port;
if (graphicsDesc.listen) graphics.$.listen = graphicsDesc.listen;
if (graphicsDesc.passwd) graphics.$.passwd = graphicsDesc.passwd;
return graphics;
},
// tslint:disable-next-line:no-any
deserialize(graphics: any): DomainGraphicsDesc {
const graphicsDesc: DomainGraphicsDesc = { };
if (graphics.$.type) graphicsDesc.type = graphics.$.type;
if (graphics.$.port) graphicsDesc.port = Number(graphics.$.port);
if (graphics.$.listen) graphicsDesc.listen = graphics.$.listen;
if (graphics.$.passwd) graphicsDesc.passwd = graphics.$.passwd;
return graphicsDesc;
},
};
export function domainDescToXml(desc: DomainDesc): string {
// tslint:disable-next-line:no-any
const domain: any = { $: { } };
if (desc.type) domain.$.type = desc.type;
if (desc.id) domain.$.id = desc.id;
if (desc.name) domain.name = desc.name;
if (desc.uuid) domain.uuid = desc.uuid;
if (desc.memory) {
domain.memory = { $: { } };
if (desc.memory.unit) domain.memory.$.unit = desc.memory.unit;
if (desc.memory.value) domain.memory._ = desc.memory.value;
}
if (desc.currentMemory) {
domain.currentMemory = { $: { } };
if (desc.currentMemory.unit) {
domain.currentMemory.$.unit = desc.currentMemory.unit;
}
if (desc.currentMemory.value) {
domain.currentMemory._ = desc.currentMemory.value;
}
}
if (desc.vcpu) {
domain.vcpu = { $: { } };
if (desc.vcpu.placement) domain.vcpu.$.placement = desc.vcpu.placement;
if (desc.vcpu.value) domain.vcpu._ = desc.vcpu.value;
}
if (desc.os) domain.os = domainOsXml.serialize(desc.os);
if (desc.devices) {
domain.devices = {
emulator: [ ],
disk: [ ],
interface: [ ],
console: [ ],
graphics: [ ],
};
for (const deviceDesc of desc.devices) {
// tslint:disable-next-line:no-any
const device: any = { $: { } };
switch (deviceDesc.type) {
case "emulator":
const emulatorDesc = deviceDesc.emulator;
if (emulatorDesc.value) device._ = emulatorDesc.value;
domain.devices.emulator.push(device);
break;
case "disk":
domain.devices.disk.push(
domainDiskXml.serialize(deviceDesc.disk));
break;
case "interface":
domain.devices.interface.push(
domainInterfaceXml.serialize(deviceDesc.interface));
break;
case "console":
const consoleDesc = deviceDesc.console;
if (consoleDesc.type) device.$.type = consoleDesc.type;
domain.devices.console.push(device);
break;
case "graphics":
domain.devices.graphics.push(
domainGraphicsXml.serialize(deviceDesc.graphics));
break;
default:
}
}
}
const builder = new xml2js.Builder({ headless: true });
return builder.buildObject({ domain });
}
export async function domainDescFromXml(xml: string): Promise<DomainDesc> {
const domainDesc: DomainDesc = { };
const parsed = await xml2js.parseStringPromise(xml);
if (!parsed.domain) throw new Error("Unable to parse domain xml");
if (parsed.domain.$.type) domainDesc.type = parsed.domain.$.type;
if (parsed.domain.$.id) domainDesc.id = parsed.domain.$.id;
if (parsed.domain.name && parsed.domain.name[0]) {
domainDesc.name = parsed.domain.name[0];
}
if (parsed.domain.uuid && parsed.domain.uuid[0]) {
domainDesc.uuid = parsed.domain.uuid[0];
}
if (parsed.domain.memory && parsed.domain.memory[0]) {
if (typeof parsed.domain.memory[0] === "object") {
domainDesc.memory = {
value: Number(parsed.domain.memory[0]._),
unit: parsed.domain.memory[0].$.unit,
};
} else if (typeof parsed.domain.memory[0] === "string") {
domainDesc.memory = {
value: Number(parsed.domain.memory[0])
};
}
}
if (parsed.domain.currentMemory && parsed.domain.currentMemory[0]) {
if (typeof parsed.domain.currentMemory[0] === "object") {
domainDesc.currentMemory = {
value: Number(parsed.domain.currentMemory[0]._),
unit: parsed.domain.currentMemory[0].$.unit,
};
} else if (typeof parsed.domain.currentMemory[0] === "string") {
domainDesc.currentMemory = {
value: Number(parsed.domain.currentMemory[0]),
};
}
}
if (parsed.domain.vcpu && parsed.domain.vcpu[0]) {
if (typeof parsed.domain.vcpu[0] === "object") {
domainDesc.vcpu = {
value: Number(parsed.domain.vcpu[0]._),
placement: parsed.domain.vcpu[0].$.placement,
};
} else if (typeof parsed.domain.vcpu[0] === "string") {
domainDesc.vcpu = {
value: Number(parsed.domain.vcpu[0]),
};
}
}
if (parsed.domain.os && parsed.domain.os[0]) {
domainDesc.os = domainOsXml.deserialize(parsed.domain.os[0]);
}
if (parsed.domain.devices && parsed.domain.devices[0]) {
domainDesc.devices = [ ];
if (parsed.domain.devices[0].emulator) {
domainDesc.devices.push({
type: "emulator",
emulator: {
value: parsed.domain.devices[0].emulator[0]
},
});
}
if (parsed.domain.devices[0].disk) {
for (const disk of parsed.domain.devices[0].disk) {
domainDesc.devices.push({
type: "disk",
disk: domainDiskXml.deserialize(disk),
});
}
}
if (parsed.domain.devices[0].interface) {
for (const iface of parsed.domain.devices[0].interface) {
domainDesc.devices.push({
type: "interface",
interface: domainInterfaceXml.deserialize(iface),
});
}
}
if (parsed.domain.devices[0].console) {
for (const cnsole of parsed.domain.devices[0].console) {
domainDesc.devices.push({
type: "console",
console: { type: cnsole.$.type },
});
}
}
if (parsed.domain.devices[0].graphics) {
for (const graphics of parsed.domain.devices[0].graphics) {
domainDesc.devices.push({
type: "graphics",
graphics: domainGraphicsXml.deserialize(graphics),
});
}
}
}
return domainDesc;
}