Files
VirtWeb/virtweb_frontend/src/api/VMApi.ts
Pierre HUBERT 8a7712ec42
All checks were successful
continuous-integration/drone/push Build is passing
Add dsmode cloud-init metadata
2025-06-07 11:50:22 +02:00

409 lines
8.2 KiB
TypeScript

/**
* Virtual Machines API
*
* @author Pierre HUBERT
*/
import { APIClient } from "./ApiClient";
export type VMState =
| "NoState"
| "Running"
| "Blocked"
| "Paused"
| "Shutdown"
| "Shutoff"
| "Crashed"
| "PowerManagementSuspended"
| "Other";
export type VMFileDisk = BaseFileVMDisk & (RawVMDisk | QCow2Disk);
export type DiskBusType = "Virtio" | "SATA";
export interface BaseFileVMDisk {
size: number;
name: string;
bus: DiskBusType;
delete: boolean;
// For new disk only
from_image?: string;
// application attributes
new?: boolean;
deleteType?: "keepfile" | "deletefile";
}
interface RawVMDisk {
format: "Raw";
is_sparse: boolean;
}
interface QCow2Disk {
format: "QCow2";
}
export interface VMNetInterfaceFilterParams {
name: string;
value: string;
}
export interface VMNetInterfaceFilter {
name: string;
parameters: VMNetInterfaceFilterParams[];
}
export type VMNetInterface = (
| VMNetUserspaceSLIRPStack
| VMNetDefinedNetwork
| VMNetBridge
) &
VMNetInterfaceBase;
export interface VMNetInterfaceBase {
mac: string;
model: "Virtio" | "E1000";
nwfilterref?: VMNetInterfaceFilter;
}
export interface VMNetUserspaceSLIRPStack {
type: "UserspaceSLIRPStack";
}
export interface VMNetDefinedNetwork {
type: "DefinedNetwork";
network: string;
}
export interface VMNetBridge {
type: "Bridge";
bridge: string;
}
export interface VMCloudInit {
attach_config: boolean;
user_data: string;
instance_id?: string;
local_hostname?: string;
dsmode?: "Net" | "Local";
network_configuration?: string;
}
export type VMBootType = "UEFI" | "UEFISecureBoot" | "Legacy";
interface VMInfoInterface {
name: string;
uuid?: string;
genid?: string;
title?: string;
description?: string;
group?: string;
boot_type: VMBootType;
architecture: "i686" | "x86_64";
memory: number;
number_vcpu: number;
vnc_access: boolean;
iso_files: string[];
file_disks: VMFileDisk[];
networks: VMNetInterface[];
tpm_module: boolean;
oem_strings: string[];
cloud_init: VMCloudInit;
}
export class VMInfo implements VMInfoInterface {
name: string;
uuid?: string;
genid?: string;
title?: string;
description?: string;
group?: string;
boot_type: VMBootType;
architecture: "i686" | "x86_64";
number_vcpu: number;
memory: number;
vnc_access: boolean;
iso_files: string[];
file_disks: VMFileDisk[];
networks: VMNetInterface[];
tpm_module: boolean;
oem_strings: string[];
cloud_init: VMCloudInit;
constructor(int: VMInfoInterface) {
this.name = int.name;
this.uuid = int.uuid;
this.genid = int.genid;
this.title = int.title;
this.description = int.description;
this.group = int.group;
this.boot_type = int.boot_type;
this.architecture = int.architecture;
this.number_vcpu = int.number_vcpu;
this.memory = int.memory;
this.vnc_access = int.vnc_access;
this.iso_files = int.iso_files;
this.file_disks = int.file_disks;
this.networks = int.networks;
this.tpm_module = int.tpm_module;
this.oem_strings = int.oem_strings;
this.cloud_init = int.cloud_init;
}
static NewEmpty(): VMInfo {
return new VMInfo({
name: "",
boot_type: "UEFI",
architecture: "x86_64",
memory: 1000 * 1000 * 1000,
number_vcpu: 1,
vnc_access: true,
iso_files: [],
file_disks: [],
networks: [],
tpm_module: true,
oem_strings: [],
cloud_init: { attach_config: false, user_data: "" },
});
}
get ViewURL(): string {
return `/vm/${this.uuid}`;
}
get EditURL(): string {
return `/vm/${this.uuid}/edit`;
}
get VNCURL(): string {
return `/vm/${this.uuid}/vnc`;
}
}
export class VMApi {
/**
* Create a new virtual machine
*/
static async Create(v: VMInfo): Promise<{ uuid: string }> {
return (
await APIClient.exec({
uri: `/vm/create`,
method: "POST",
jsonData: v,
})
).data;
}
/**
* Get the list of defined virtual machines
*/
static async GetList(): Promise<VMInfo[]> {
return (
await APIClient.exec({
uri: "/vm/list",
method: "GET",
})
).data.map((i: VMInfoInterface) => new VMInfo(i));
}
/**
* Get the information about a single VM
*/
static async GetSingle(uuid: string): Promise<VMInfo> {
const data = (
await APIClient.exec({
uri: `/vm/${uuid}`,
method: "GET",
})
).data;
return new VMInfo(data);
}
/**
* Get the source XML configuration of a domain for debugging purposes
*/
static async GetSingleXML(uuid: string): Promise<string> {
return (
await APIClient.exec({
uri: `/vm/${uuid}/src`,
method: "GET",
})
).data;
}
/**
* Update the information about a single VM
*/
static async UpdateSingle(vm: VMInfo): Promise<VMInfo> {
// Process disks list, looking for removal
vm.file_disks = vm.file_disks.filter((d) => d.deleteType !== "keepfile");
vm.file_disks.forEach((d) => {
if (d.deleteType === "deletefile") d.delete = true;
});
const data = (
await APIClient.exec({
uri: `/vm/${vm.uuid!}`,
method: "PUT",
jsonData: vm,
})
).data;
return new VMInfo(data);
}
/**
* Check if autostart is enabled on a VM
*/
static async IsAutostart(vm: VMInfo): Promise<boolean> {
return (
await APIClient.exec({
uri: `/vm/${vm.uuid}/autostart`,
method: "GET",
})
).data.autostart;
}
/**
* Set autostart status of a VM
*/
static async SetAutostart(vm: VMInfo, enabled: boolean): Promise<void> {
await APIClient.exec({
uri: `/vm/${vm.uuid}/autostart`,
method: "PUT",
jsonData: { autostart: enabled },
});
}
/**
* Get the state of a VM
*/
static async GetState(vm: VMInfo): Promise<VMState> {
return (
await APIClient.exec({
uri: `/vm/${vm.uuid}/state`,
method: "GET",
})
).data.state;
}
/**
* Get a screenshot of a VM
*/
static async Screenshot(vm: VMInfo): Promise<Blob> {
return (
await APIClient.exec({
uri: `/vm/${vm.uuid}/screenshot`,
method: "GET",
})
).data;
}
/**
* Start the VM
*/
static async StartVM(vm: VMInfo): Promise<void> {
return (
await APIClient.exec({
uri: `/vm/${vm.uuid}/start`,
method: "GET",
})
).data.state;
}
/**
* Shutdown the VM
*/
static async ShutdownVM(vm: VMInfo): Promise<void> {
return (
await APIClient.exec({
uri: `/vm/${vm.uuid}/shutdown`,
method: "GET",
})
).data.state;
}
/**
* Restt the VM
*/
static async ResetVM(vm: VMInfo): Promise<void> {
return (
await APIClient.exec({
uri: `/vm/${vm.uuid}/reset`,
method: "GET",
})
).data.state;
}
/**
* Kill the VM
*/
static async KillVM(vm: VMInfo): Promise<void> {
return (
await APIClient.exec({
uri: `/vm/${vm.uuid}/kill`,
method: "GET",
})
).data.state;
}
/**
* Suspend the VM
*/
static async SuspendVM(vm: VMInfo): Promise<void> {
return (
await APIClient.exec({
uri: `/vm/${vm.uuid}/suspend`,
method: "GET",
})
).data.state;
}
/**
* Resume the VM
*/
static async ResumeVM(vm: VMInfo): Promise<void> {
return (
await APIClient.exec({
uri: `/vm/${vm.uuid}/resume`,
method: "GET",
})
).data.state;
}
/**
* Delete a virtual machine
*/
static async Delete(vm: VMInfo, keep_files: boolean): Promise<void> {
await APIClient.exec({
uri: `/vm/${vm.uuid}`,
method: "DELETE",
jsonData: { keep_files },
});
}
/**
* Get a oneshot VNC connect URL
*/
static async OneShotVNCURL(vm: VMInfo): Promise<string> {
const token = (
await APIClient.exec({
uri: `/vm/${vm.uuid}/vnc_token`,
method: "GET",
})
).data.token;
let baseWSURL = APIClient.backendURL();
if (!baseWSURL.includes("://"))
baseWSURL =
window.location.protocol + "//" + window.location.host + baseWSURL;
return (
baseWSURL.replace("http", "ws") +
"/vnc?token=" +
encodeURIComponent(token)
);
}
}