/** * 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; // Resize disk image after clone resize?: boolean; // application attributes new?: boolean; originalSize?: number; 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 { 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 { 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 { return ( await APIClient.exec({ uri: `/vm/${uuid}/src`, method: "GET", }) ).data; } /** * Update the information about a single VM */ static async UpdateSingle(vm: VMInfo): Promise { // 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 { 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 { 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 { 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 { return ( await APIClient.exec({ uri: `/vm/${vm.uuid}/screenshot`, method: "GET", }) ).data; } /** * Start the VM */ static async StartVM(vm: VMInfo): Promise { return ( await APIClient.exec({ uri: `/vm/${vm.uuid}/start`, method: "GET", }) ).data.state; } /** * Shutdown the VM */ static async ShutdownVM(vm: VMInfo): Promise { return ( await APIClient.exec({ uri: `/vm/${vm.uuid}/shutdown`, method: "GET", }) ).data.state; } /** * Restt the VM */ static async ResetVM(vm: VMInfo): Promise { return ( await APIClient.exec({ uri: `/vm/${vm.uuid}/reset`, method: "GET", }) ).data.state; } /** * Kill the VM */ static async KillVM(vm: VMInfo): Promise { return ( await APIClient.exec({ uri: `/vm/${vm.uuid}/kill`, method: "GET", }) ).data.state; } /** * Suspend the VM */ static async SuspendVM(vm: VMInfo): Promise { return ( await APIClient.exec({ uri: `/vm/${vm.uuid}/suspend`, method: "GET", }) ).data.state; } /** * Resume the VM */ static async ResumeVM(vm: VMInfo): Promise { 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 { await APIClient.exec({ uri: `/vm/${vm.uuid}`, method: "DELETE", jsonData: { keep_files }, }); } /** * Get a oneshot VNC connect URL */ static async OneShotVNCURL(vm: VMInfo): Promise { 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) ); } }