ts-wasm-libvirt
v0.1.8
Published
TypeScript adapter for libvirt backed by Rust/WASM and virt
Readme
ts-wasm-libvirt
TypeScript-клиент для libvirt: домены, сети, хранилища, устройства, secrets, snapshots и бинарные потоки. Нативный host-пакет для поддерживаемой ОС и архитектуры устанавливается автоматически как optional dependency.
Установка и зависимости
Нужны Node.js 20+ и установленная системная библиотека libvirt. Для соединения с локальным QEMU также должен работать демон libvirt, а у пользователя должен быть доступ к его сокету.
Debian/Ubuntu:
sudo apt install libvirt0 libvirt-daemon-system
npm install ts-wasm-libvirtДля проверки без гипервизора используйте URI test:///default. На Linux доступны готовые host-пакеты для x64 и arm64; на macOS — для arm64.
Все методы ниже принадлежат LibvirtClient, который создаётся функцией createLibvirtClient. Во всех примерах предполагается следующий контекст; закрывайте клиент в finally.
import { createLibvirtClient } from "ts-wasm-libvirt";
const client = await createLibvirtClient({ uri: "qemu:///system" });
const domain = { name: "my-vm" };
const network = { name: "default" };
const hostInterface = { name: "eth0" };
const pool = { name: "default-pool" };
const volume = { path: "/var/lib/libvirt/images/my-vm.qcow2" };
const networkFilter = { name: "my-filter" };
const secret = { uuid: "11111111-2222-3333-4444-555555555555" };
const domainXml = "<domain type='kvm'><name>my-vm</name></domain>";
const networkXml = "<network><name>my-network</name></network>";
const interfaceXml = "<interface type='ethernet'><name>eth0</name></interface>";
const poolXml = "<pool type='dir'><name>my-pool</name></pool>";
const volumeXml = "<volume><name>disk.qcow2</name></volume>";
const nodeDeviceXml = "<device><name>my-device</name></device>";
const networkFilterXml = "<filter name='my-filter' chain='root'/>";
const secretXml = "<secret ephemeral='no' private='no'/>";
const snapshotXml = "<domainsnapshot><name>before-update</name></domainsnapshot>";
try {
// Use one or more methods from this catalogue.
} finally {
await client.close();
}const client = await createLibvirtClient({
uri: "qemu:///system",
defaultTimeoutMs: 5_000,
});import { LibvirtCredentialType, createLibvirtClient } from "ts-wasm-libvirt";
const client = await createLibvirtClient({
uri: "qemu+ssh://host/system",
auth: {
credentialTypes: [LibvirtCredentialType.AuthName, LibvirtCredentialType.Passphrase],
callback(credentials) {
return credentials.map((credential) => credential.defaultResult);
},
},
});Возможности API
Селекторы выбирают ресурс по одному идентификатору: домен — { name } или { uuid }, сеть — { name } или { uuid }, pool — { name }, { uuid } или { path }, volume — { key } или { path }. Опциональный последний аргумент options принимает timeoutMs и signal; методы с флагами также принимают flags.
Подключение и базовые операции
const result = await client.health();
console.log(result);const result = await client.listDomains();
console.log(result);const result = await client.getDomain(domain);
console.log(result);const result = await client.getDomainXml(domain);
console.log(result);const result = await client.defineDomain(domainXml);
console.log(result);await client.undefineDomain(domain);const result = await client.startDomain(domain);
console.log(result);const result = await client.shutdownDomain(domain);
console.log(result);const result = await client.destroyDomain(domain);
console.log(result);const result = await client.rebootDomain(domain);
console.log(result);Домены: жизненный цикл, устройства и миграция
const result = await client.getDomainInfo(domain);
console.log(result);const result = await client.suspendDomain(domain);
console.log(result);const result = await client.resumeDomain(domain);
console.log(result);const result = await client.resetDomain(domain);
console.log(result);const result = await client.getDomainAutostart(domain);
console.log(result);await client.setDomainAutostart(domain, true);const result = await client.isDomainActive(domain);
console.log(result);const result = await client.isDomainPersistent(domain);
console.log(result);const result = await client.getDomainOsType(domain);
console.log(result);const result = await client.getDomainHostname(domain);
console.log(result);const result = await client.getDomainMaxMemory(domain);
console.log(result);await client.setDomainMemory(domain, 2_097_152);await client.setDomainMaxMemory(domain, 2_097_152);const result = await client.getDomainMaxVcpus(domain);
console.log(result);await client.setDomainVcpus(domain, 2);const result = await client.createTransientDomain(domainXml);
console.log(result);const result = await client.isDomainUpdated(domain);
console.log(result);const result = await client.wakeupDomain(domain);
console.log(result);const result = await client.getDomainVcpusFlags(domain);
console.log(result);await client.setDomainVcpusFlags(domain, 2);const result = await client.getDomainTime(domain);
console.log(result);await client.setDomainTime(domain, { seconds: 0n, nanoseconds: 0 });const result = await client.getDomainBlockInfo(domain, "vda");
console.log(result);const result = await client.getDomainBlockStats(domain, "vda");
console.log(result);await client.resizeDomainBlock(domain, "vda", 10_737_418_240n);const result = await client.getDomainInterfaceStats(hostInterface, "/var/lib/libvirt/images/my-vm.qcow2");
console.log(result);const result = await client.getDomainMemoryStats(domain);
console.log(result);const result = await client.getDomainInterfaceAddresses(hostInterface, 0);
console.log(result);const result = await client.getDomainMemoryParameters(domain);
console.log(result);await client.setDomainMemoryParameters(domain, { /* typed parameters */ });const result = await client.getDomainNumaParameters(domain);
console.log(result);await client.setDomainNumaParameters(domain, { /* typed parameters */ });await client.pinDomainVcpu(domain, 0, new Uint8Array([0b0000_0011]));await client.pinDomainEmulator(domain, new Uint8Array([0b0000_0011]));await client.sendDomainKey(domain, 0, 100, [30]);const result = await client.getDomainSchedulerParameters(domain);
console.log(result);await client.setDomainSchedulerParameters(domain, { /* typed parameters */ });const result = await client.getDomainJobInfo(domain);
console.log(result);const result = await client.getDomainJobStats(domain);
console.log(result);const result = await client.attachDomainDevice(domain, domainXml);
console.log(result);const result = await client.detachDomainDevice(domain, domainXml);
console.log(result);const result = await client.updateDomainDevice(domain, domainXml);
console.log(result);await client.managedSaveDomain(domain);const result = await client.hasDomainManagedSave(domain);
console.log(result);await client.removeDomainManagedSave(domain);await client.coreDumpDomain(domain, "/var/lib/libvirt/images/my-vm.qcow2");const result = await client.getDomainMetadata(domain, 0);
console.log(result);await client.setDomainMetadata(domain, 0, "<metadata/>");const result = await client.renameDomain(domain, "renamed-vm");
console.log(result);await client.setDomainUserPassword(domain, "guest-user", "secret");const result = await client.qemuMonitorCommand(domain, "query-status");
console.log(result);const result = await client.qemuAgentCommand(domain, "query-status");
console.log(result);await client.restoreDomain("/var/lib/libvirt/images/my-vm.qcow2");const result = await client.getDomainSaveImageXml("/var/lib/libvirt/images/my-vm.qcow2");
console.log(result);await client.setDomainSaveImageXml("/var/lib/libvirt/images/my-vm.qcow2", domainXml);await client.migrateDomainToUri(domain, "qemu+ssh://destination/system");const result = await client.migrateDomainToConnection(domain, "qemu+ssh://destination/system");
console.log(result);const stream = await client.screenshotDomain(domain);
// Read: for await (const chunk of stream) { /* Buffer */ }
// Write: stream.write(data); stream.end();
await stream.finish();const stream = await client.openDomainConsole(domain);
// Read: for await (const chunk of stream) { /* Buffer */ }
// Write: stream.write(data); stream.end();
await stream.finish();const stream = await client.openDomainChannel(domain);
// Read: for await (const chunk of stream) { /* Buffer */ }
// Write: stream.write(data); stream.end();
await stream.finish();const stream = await client.openDomainGraphics(domain);
// Read: for await (const chunk of stream) { /* Buffer */ }
// Write: stream.write(data); stream.end();
await stream.finish();Соединение, compute node и CPU
const result = await client.getCapabilities();
console.log(result);const result = await client.getConnectionHostname();
console.log(result);const result = await client.getLibvirtVersion();
console.log(result);const result = await client.getHypervisorVersion();
console.log(result);const result = await client.getSystemInfo();
console.log(result);const result = await client.isConnectionSecure();
console.log(result);const result = await client.isConnectionEncrypted();
console.log(result);const result = await client.getFreeMemory();
console.log(result);const result = await client.getMaxVcpus();
console.log(result);const result = await client.getNodeInfo();
console.log(result);const result = await client.getCpuModels("x86_64");
console.log(result);const result = await client.compareCpu(domainXml);
console.log(result);const result = await client.baselineCpu([domainXml]);
console.log(result);const result = await client.getDomainCapabilities();
console.log(result);const result = await client.domainXmlFromNative("qemu", "native configuration");
console.log(result);const result = await client.domainXmlToNative("qemu", domainXml);
console.log(result);const result = await client.setConnectionKeepalive(5, 3);
console.log(result);const result = await client.getCellsFreeMemory(0, 2);
console.log(result);const result = await client.getFreePages([4], 0, 2);
console.log(result);const result = await client.findStoragePoolSources(0);
console.log(result);Сети
const result = await client.listNetworks();
console.log(result);const result = await client.getNetwork(network);
console.log(result);const result = await client.getNetworkXml(network);
console.log(result);const result = await client.defineNetwork(networkXml);
console.log(result);const result = await client.createNetwork(networkXml);
console.log(result);const result = await client.startNetwork(network);
console.log(result);await client.destroyNetwork(network);await client.undefineNetwork(network);await client.setNetworkAutostart(network, true);const result = await client.getNetworkBridgeName(network);
console.log(result);await client.updateNetwork(network, networkXml, { command: 0, section: 0 });Интерфейсы хоста
const result = await client.listInterfaces();
console.log(result);const result = await client.getInterface(hostInterface);
console.log(result);const result = await client.getInterfaceXml(hostInterface);
console.log(result);const result = await client.defineInterface(interfaceXml);
console.log(result);const result = await client.startInterface(hostInterface);
console.log(result);const result = await client.destroyInterface(hostInterface);
console.log(result);await client.undefineInterface(hostInterface);Storage pools и volumes
const result = await client.listStoragePools();
console.log(result);const result = await client.getStoragePool(pool);
console.log(result);const result = await client.getStoragePoolXml(pool);
console.log(result);const result = await client.defineStoragePool(poolXml);
console.log(result);const result = await client.createStoragePool(poolXml);
console.log(result);const result = await client.startStoragePool(pool);
console.log(result);const result = await client.buildStoragePool(pool);
console.log(result);const result = await client.refreshStoragePool(pool);
console.log(result);await client.destroyStoragePool(pool);await client.deleteStoragePool(pool);await client.undefineStoragePool(pool);await client.setStoragePoolAutostart(pool, true);const result = await client.listStorageVolumes(volume);
console.log(result);const result = await client.getStoragePoolByVolume(volume);
console.log(result);const result = await client.getStorageVolume(volume);
console.log(result);const result = await client.getStorageVolumeXml(volume);
console.log(result);const result = await client.createStorageVolume(pool, volumeXml);
console.log(result);await client.deleteStorageVolume(volume);await client.wipeStorageVolume(volume);await client.resizeStorageVolume(volume, 10_737_418_240n);const result = await client.getStorageVolumeByName(pool, "resource-name");
console.log(result);const result = await client.cloneStorageVolume(pool, volumeXml, volume);
console.log(result);await client.wipeStorageVolumePattern(volume, 0);const stream = await client.downloadStorageVolume(volume);
// Read: for await (const chunk of stream) { /* Buffer */ }
// Write: stream.write(data); stream.end();
await stream.finish();const stream = await client.uploadStorageVolume(volume);
// Read: for await (const chunk of stream) { /* Buffer */ }
// Write: stream.write(data); stream.end();
await stream.finish();Устройства хоста
const result = await client.listNodeDevices();
console.log(result);const result = await client.getNodeDevice("resource-name");
console.log(result);const result = await client.getNodeDeviceXml("resource-name");
console.log(result);const result = await client.createNodeDevice(nodeDeviceXml);
console.log(result);await client.destroyNodeDevice("resource-name");const result = await client.detachNodeDevice("resource-name");
console.log(result);const result = await client.resetNodeDevice("resource-name");
console.log(result);const result = await client.reattachNodeDevice("resource-name");
console.log(result);const result = await client.getScsiHostNodeDevice("50014380242b9751", "50014380242b9752");
console.log(result);const result = await client.detachNodeDeviceFlags("resource-name");
console.log(result);const result = await client.countNodeDevices();
console.log(result);Network filters
const result = await client.listNetworkFilters();
console.log(result);const result = await client.getNetworkFilter(networkFilter);
console.log(result);const result = await client.getNetworkFilterXml(networkFilter);
console.log(result);const result = await client.defineNetworkFilter(networkFilterXml);
console.log(result);await client.undefineNetworkFilter(networkFilter);Secrets
const result = await client.listSecrets();
console.log(result);const result = await client.getSecret(secret);
console.log(result);const result = await client.getSecretXml(secret);
console.log(result);const result = await client.defineSecret(secretXml);
console.log(result);const result = await client.getSecretValue(secret);
console.log(result);await client.setSecretValue(secret, new Uint8Array([1, 2, 3]));await client.undefineSecret(secret);Снимки доменов и закрытие
const result = await client.listDomainSnapshots(domain);
console.log(result);const result = await client.getDomainSnapshot(domain, "resource-name");
console.log(result);const result = await client.getDomainSnapshotXml(domain, "resource-name");
console.log(result);const result = await client.createDomainSnapshot(domain, snapshotXml);
console.log(result);const result = await client.getCurrentDomainSnapshot(domain);
console.log(result);const result = await client.revertDomainSnapshot(domain, "resource-name");
console.log(result);await client.deleteDomainSnapshot(domain, "resource-name");const result = await client.listDomainSnapshotChildren(domain, "resource-name");
console.log(result);const result = await client.getDomainSnapshotParent(domain, "resource-name");
console.log(result);await client.close();Ошибки и потоки
import { LibvirtAdapterError } from "ts-wasm-libvirt";
try {
await client.getDomain({ name: "missing-vm" });
} catch (error) {
if (error instanceof LibvirtAdapterError && error.code === "NOT_FOUND") {
console.log("Домен не найден");
}
}Коды: INITIALIZATION_FAILED, HOST_ERROR, INVALID_ARGUMENT, NOT_FOUND, CONFLICT, INVALID_DEFINITION, CANCELLED, TIMEOUT.
const stream = await client.uploadStorageVolume(volume, { length: 4 });
stream.end(Buffer.from([1, 2, 3, 4]));
await stream.finish();const stream = await client.openDomainConsole(domain);
// On cancellation or error:
await stream.abort();Значения libvirt u64, которые не всегда точны в JavaScript, возвращаются как десятичные строки UInt64String; соответствующие входные параметры принимают number | bigint.
