Can set the number of VCPUs
This commit is contained in:
parent
d1a9b6c3bb
commit
5f0f56a9f9
47
virtweb_backend/Cargo.lock
generated
47
virtweb_backend/Cargo.lock
generated
@ -1624,6 +1624,40 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
@ -1634,6 +1668,17 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-rational"
|
name = "num-rational"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -1641,6 +1686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
|
"num-bigint",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
@ -2583,6 +2629,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"light-openid",
|
"light-openid",
|
||||||
"log",
|
"log",
|
||||||
|
"num",
|
||||||
"rand",
|
"rand",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -40,3 +40,4 @@ bytes = "1.5.0"
|
|||||||
tokio = "1.32.0"
|
tokio = "1.32.0"
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
ipnetwork = "0.20.0"
|
ipnetwork = "0.20.0"
|
||||||
|
num = "0.4.1"
|
@ -91,6 +91,23 @@ pub async fn server_info(client: LibVirtReq) -> HttpResult {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn number_vcpus() -> HttpResult {
|
||||||
|
let mut system = System::new();
|
||||||
|
system.refresh_cpu();
|
||||||
|
let number_cpus = system.cpus().len();
|
||||||
|
assert_ne!(number_cpus, 0, "Got invlid number of CPU!");
|
||||||
|
|
||||||
|
let mut possible_numbers = vec![1];
|
||||||
|
|
||||||
|
if number_cpus > 1 {
|
||||||
|
for i in 0..(number_cpus / 2) {
|
||||||
|
possible_numbers.push(2 + 2 * i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(possible_numbers))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn networks_list() -> HttpResult {
|
pub async fn networks_list() -> HttpResult {
|
||||||
let mut system = System::new();
|
let mut system = System::new();
|
||||||
system.refresh_networks_list();
|
system.refresh_networks_list();
|
||||||
|
@ -192,6 +192,13 @@ pub struct DomainCPUTopology {
|
|||||||
pub threads: usize,
|
pub threads: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(rename = "cpu")]
|
||||||
|
pub struct DomainVCPUXML {
|
||||||
|
#[serde(rename = "$value")]
|
||||||
|
pub body: usize,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(rename = "cpu")]
|
#[serde(rename = "cpu")]
|
||||||
pub struct DomainCPUXML {
|
pub struct DomainCPUXML {
|
||||||
@ -221,6 +228,9 @@ pub struct DomainXML {
|
|||||||
/// The maximum allocation of memory for the guest at boot time
|
/// The maximum allocation of memory for the guest at boot time
|
||||||
pub memory: DomainMemoryXML,
|
pub memory: DomainMemoryXML,
|
||||||
|
|
||||||
|
/// Number of vCPU
|
||||||
|
pub vcpu: DomainVCPUXML,
|
||||||
|
|
||||||
/// CPU information
|
/// CPU information
|
||||||
pub cpu: DomainCPUXML,
|
pub cpu: DomainCPUXML,
|
||||||
|
|
||||||
|
@ -2,15 +2,17 @@ use crate::app_config::AppConfig;
|
|||||||
use crate::constants;
|
use crate::constants;
|
||||||
use crate::libvirt_lib_structures::{
|
use crate::libvirt_lib_structures::{
|
||||||
DevicesXML, DiskBootXML, DiskDriverXML, DiskReadOnlyXML, DiskSourceXML, DiskTargetXML, DiskXML,
|
DevicesXML, DiskBootXML, DiskDriverXML, DiskReadOnlyXML, DiskSourceXML, DiskTargetXML, DiskXML,
|
||||||
DomainCPUTopology, DomainCPUXML, DomainMemoryXML, DomainXML, FeaturesXML, GraphicsXML,
|
DomainCPUTopology, DomainCPUXML, DomainMemoryXML, DomainVCPUXML, DomainXML, FeaturesXML,
|
||||||
NetworkDHCPRangeXML, NetworkDHCPXML, NetworkDNSForwarderXML, NetworkDNSXML, NetworkDomainXML,
|
GraphicsXML, NetworkDHCPRangeXML, NetworkDHCPXML, NetworkDNSForwarderXML, NetworkDNSXML,
|
||||||
NetworkForwardXML, NetworkIPXML, NetworkXML, OSLoaderXML, OSTypeXML, XMLUuid, ACPIXML, OSXML,
|
NetworkDomainXML, NetworkForwardXML, NetworkIPXML, NetworkXML, OSLoaderXML, OSTypeXML, XMLUuid,
|
||||||
|
ACPIXML, OSXML,
|
||||||
};
|
};
|
||||||
use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
|
use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
|
||||||
use crate::utils::disks_utils::Disk;
|
use crate::utils::disks_utils::Disk;
|
||||||
use crate::utils::files_utils;
|
use crate::utils::files_utils;
|
||||||
use ipnetwork::{Ipv4Network, Ipv6Network};
|
use ipnetwork::{Ipv4Network, Ipv6Network};
|
||||||
use lazy_regex::regex;
|
use lazy_regex::regex;
|
||||||
|
use num::Integer;
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||||
use std::ops::{Div, Mul};
|
use std::ops::{Div, Mul};
|
||||||
|
|
||||||
@ -74,6 +76,8 @@ pub struct VMInfo {
|
|||||||
pub architecture: VMArchitecture,
|
pub architecture: VMArchitecture,
|
||||||
/// VM allocated memory, in megabytes
|
/// VM allocated memory, in megabytes
|
||||||
pub memory: usize,
|
pub memory: usize,
|
||||||
|
/// Number of vCPU for the VM
|
||||||
|
pub number_vcpu: usize,
|
||||||
/// Enable VNC access through admin console
|
/// Enable VNC access through admin console
|
||||||
pub vnc_access: bool,
|
pub vnc_access: bool,
|
||||||
/// Attach an ISO file
|
/// Attach an ISO file
|
||||||
@ -81,7 +85,6 @@ pub struct VMInfo {
|
|||||||
/// Storage - https://access.redhat.com/documentation/fr-fr/red_hat_enterprise_linux/6/html/virtualization_administration_guide/sect-virtualization-virtualized_block_devices-adding_storage_devices_to_guests#sect-Virtualization-Adding_storage_devices_to_guests-Adding_file_based_storage_to_a_guest
|
/// Storage - https://access.redhat.com/documentation/fr-fr/red_hat_enterprise_linux/6/html/virtualization_administration_guide/sect-virtualization-virtualized_block_devices-adding_storage_devices_to_guests#sect-Virtualization-Adding_storage_devices_to_guests-Adding_file_based_storage_to_a_guest
|
||||||
pub disks: Vec<Disk>,
|
pub disks: Vec<Disk>,
|
||||||
// TODO : network interfaces
|
// TODO : network interfaces
|
||||||
// TODO : number of CPUs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VMInfo {
|
impl VMInfo {
|
||||||
@ -116,6 +119,10 @@ impl VMInfo {
|
|||||||
return Err(StructureExtraction("VM memory is invalid!").into());
|
return Err(StructureExtraction("VM memory is invalid!").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.number_vcpu == 0 || (self.number_vcpu != 1 && self.number_vcpu.is_odd()) {
|
||||||
|
return Err(StructureExtraction("Invalid number of vCPU specified!").into());
|
||||||
|
}
|
||||||
|
|
||||||
let mut disks = vec![];
|
let mut disks = vec![];
|
||||||
|
|
||||||
if let Some(iso_file) = &self.iso_file {
|
if let Some(iso_file) = &self.iso_file {
|
||||||
@ -245,12 +252,22 @@ impl VMInfo {
|
|||||||
memory: self.memory,
|
memory: self.memory,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
vcpu: DomainVCPUXML {
|
||||||
|
body: self.number_vcpu,
|
||||||
|
},
|
||||||
|
|
||||||
cpu: DomainCPUXML {
|
cpu: DomainCPUXML {
|
||||||
mode: "host-passthrough".to_string(),
|
mode: "host-passthrough".to_string(),
|
||||||
topology: Some(DomainCPUTopology {
|
topology: Some(DomainCPUTopology {
|
||||||
sockets: 1,
|
sockets: 1,
|
||||||
cores: 1,
|
cores: match self.number_vcpu {
|
||||||
threads: 1,
|
1 => 1,
|
||||||
|
v => v / 2,
|
||||||
|
},
|
||||||
|
threads: match self.number_vcpu {
|
||||||
|
1 => 1,
|
||||||
|
_ => 2,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -285,6 +302,7 @@ impl VMInfo {
|
|||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
number_vcpu: domain.vcpu.body,
|
||||||
memory: convert_to_mb(&domain.memory.unit, domain.memory.memory)?,
|
memory: convert_to_mb(&domain.memory.unit, domain.memory.memory)?,
|
||||||
vnc_access: domain.devices.graphics.is_some(),
|
vnc_access: domain.devices.graphics.is_some(),
|
||||||
iso_file: domain
|
iso_file: domain
|
||||||
|
@ -102,6 +102,10 @@ async fn main() -> std::io::Result<()> {
|
|||||||
"/api/server/info",
|
"/api/server/info",
|
||||||
web::get().to(server_controller::server_info),
|
web::get().to(server_controller::server_info),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/api/server/number_vcpus",
|
||||||
|
web::get().to(server_controller::number_vcpus),
|
||||||
|
)
|
||||||
.route(
|
.route(
|
||||||
"/api/server/networks",
|
"/api/server/networks",
|
||||||
web::get().to(server_controller::networks_list),
|
web::get().to(server_controller::networks_list),
|
||||||
|
@ -172,6 +172,18 @@ export class ServerApi {
|
|||||||
).data;
|
).data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get host supported vCPUs configurations
|
||||||
|
*/
|
||||||
|
static async NumberVCPUs(): Promise<number[]> {
|
||||||
|
return (
|
||||||
|
await APIClient.exec({
|
||||||
|
method: "GET",
|
||||||
|
uri: "/server/number_vcpus",
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get host networks card list
|
* Get host networks card list
|
||||||
*/
|
*/
|
||||||
|
@ -39,6 +39,7 @@ interface VMInfoInterface {
|
|||||||
boot_type: "UEFI" | "UEFISecureBoot";
|
boot_type: "UEFI" | "UEFISecureBoot";
|
||||||
architecture: "i686" | "x86_64";
|
architecture: "i686" | "x86_64";
|
||||||
memory: number;
|
memory: number;
|
||||||
|
number_vcpu: number;
|
||||||
vnc_access: boolean;
|
vnc_access: boolean;
|
||||||
iso_file?: string;
|
iso_file?: string;
|
||||||
disks: VMDisk[];
|
disks: VMDisk[];
|
||||||
@ -52,6 +53,7 @@ export class VMInfo implements VMInfoInterface {
|
|||||||
description?: string;
|
description?: string;
|
||||||
boot_type: "UEFI" | "UEFISecureBoot";
|
boot_type: "UEFI" | "UEFISecureBoot";
|
||||||
architecture: "i686" | "x86_64";
|
architecture: "i686" | "x86_64";
|
||||||
|
number_vcpu: number;
|
||||||
memory: number;
|
memory: number;
|
||||||
vnc_access: boolean;
|
vnc_access: boolean;
|
||||||
iso_file?: string;
|
iso_file?: string;
|
||||||
@ -65,6 +67,7 @@ export class VMInfo implements VMInfoInterface {
|
|||||||
this.description = int.description;
|
this.description = int.description;
|
||||||
this.boot_type = int.boot_type;
|
this.boot_type = int.boot_type;
|
||||||
this.architecture = int.architecture;
|
this.architecture = int.architecture;
|
||||||
|
this.number_vcpu = int.number_vcpu;
|
||||||
this.memory = int.memory;
|
this.memory = int.memory;
|
||||||
this.vnc_access = int.vnc_access;
|
this.vnc_access = int.vnc_access;
|
||||||
this.iso_file = int.iso_file;
|
this.iso_file = int.iso_file;
|
||||||
@ -77,6 +80,7 @@ export class VMInfo implements VMInfoInterface {
|
|||||||
boot_type: "UEFI",
|
boot_type: "UEFI",
|
||||||
architecture: "x86_64",
|
architecture: "x86_64",
|
||||||
memory: 1024,
|
memory: 1024,
|
||||||
|
number_vcpu: 1,
|
||||||
vnc_access: true,
|
vnc_access: true,
|
||||||
disks: [],
|
disks: [],
|
||||||
});
|
});
|
||||||
|
@ -80,7 +80,7 @@ function VNCInner(p: { vm: VMInfo }): React.ReactElement {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<VncScreen
|
<VncScreen
|
||||||
url={token!.url}
|
url={token.url}
|
||||||
onDisconnect={() => {
|
onDisconnect={() => {
|
||||||
console.info("VNC disconnected " + token?.url);
|
console.info("VNC disconnected " + token?.url);
|
||||||
disconnected();
|
disconnected();
|
||||||
|
@ -22,10 +22,14 @@ interface DetailsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function VMDetails(p: DetailsProps): React.ReactElement {
|
export function VMDetails(p: DetailsProps): React.ReactElement {
|
||||||
const [list, setList] = React.useState<IsoFile[] | any>();
|
const [isoList, setIsoList] = React.useState<IsoFile[] | any>();
|
||||||
|
const [vcpuCombinations, setVCPUCombinations] = React.useState<
|
||||||
|
number[] | any
|
||||||
|
>();
|
||||||
|
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
setList(await IsoFilesApi.GetList());
|
setIsoList(await IsoFilesApi.GetList());
|
||||||
|
setVCPUCombinations(await ServerApi.NumberVCPUs());
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -33,13 +37,19 @@ export function VMDetails(p: DetailsProps): React.ReactElement {
|
|||||||
loadKey={"1"}
|
loadKey={"1"}
|
||||||
load={load}
|
load={load}
|
||||||
errMsg="Failed to load the list of ISO files"
|
errMsg="Failed to load the list of ISO files"
|
||||||
build={() => <VMDetailsInner isoList={list} {...p} />}
|
build={() => (
|
||||||
|
<VMDetailsInner
|
||||||
|
isoList={isoList}
|
||||||
|
vcpuCombinations={vcpuCombinations}
|
||||||
|
{...p}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function VMDetailsInner(
|
function VMDetailsInner(
|
||||||
p: DetailsProps & { isoList: IsoFile[] }
|
p: DetailsProps & { isoList: IsoFile[]; vcpuCombinations: number[] }
|
||||||
): React.ReactElement {
|
): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
@ -146,6 +156,19 @@ function VMDetailsInner(
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SelectInput
|
||||||
|
editable={p.editable}
|
||||||
|
label="Number of vCPU"
|
||||||
|
options={p.vcpuCombinations.map((v) => {
|
||||||
|
return { label: v.toString(), value: v.toString() };
|
||||||
|
})}
|
||||||
|
value={p.vm.number_vcpu.toString()}
|
||||||
|
onValueChange={(v) => {
|
||||||
|
p.vm.number_vcpu = Number(v ?? "0");
|
||||||
|
p.onChange?.();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<CheckboxInput
|
<CheckboxInput
|
||||||
editable={p.editable}
|
editable={p.editable}
|
||||||
label="Enable VNC access"
|
label="Enable VNC access"
|
||||||
|
Loading…
Reference in New Issue
Block a user