Simplify RAM management
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Pierre HUBERT 2025-05-30 09:20:49 +02:00
parent dd7f9176fa
commit a18310e04a
7 changed files with 75 additions and 62 deletions

View File

@ -36,11 +36,11 @@ pub const ALLOWED_DISK_IMAGES_MIME_TYPES: [&str; 2] =
/// Disk image max size
pub const DISK_IMAGE_MAX_SIZE: FileSize = FileSize::from_gb(10 * 1000);
/// Min VM memory size (MB)
pub const MIN_VM_MEMORY: usize = 100;
/// Min VM memory size
pub const MIN_VM_MEMORY: FileSize = FileSize::from_mb(100);
/// Max VM memory size (MB)
pub const MAX_VM_MEMORY: usize = 64000;
/// Max VM memory size
pub const MAX_VM_MEMORY: FileSize = FileSize::from_gb(64);
/// Disk name min length
pub const DISK_NAME_MIN_LEN: usize = 2;

View File

@ -80,8 +80,8 @@ pub async fn static_config(local_auth: LocalAuthEnabled) -> impl Responder {
vm_title_size: LenConstraints { min: 0, max: 50 },
group_id_size: LenConstraints { min: 3, max: 50 },
memory_size: LenConstraints {
min: constants::MIN_VM_MEMORY,
max: constants::MAX_VM_MEMORY,
min: constants::MIN_VM_MEMORY.as_bytes(),
max: constants::MAX_VM_MEMORY.as_bytes(),
},
disk_name_size: LenConstraints {
min: DISK_NAME_MIN_LEN,

View File

@ -4,8 +4,8 @@ use crate::libvirt_lib_structures::XMLUuid;
use crate::libvirt_lib_structures::domain::*;
use crate::libvirt_rest_structures::LibVirtStructError;
use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
use crate::utils::file_size_utils::FileSize;
use crate::utils::files_utils;
use crate::utils::files_utils::convert_size_unit_to_mb;
use crate::utils::vm_file_disks_utils::{VMDiskFormat, VMFileDisk};
use lazy_regex::regex;
use num::Integer;
@ -70,8 +70,8 @@ pub struct VMInfo {
pub group: Option<VMGroupId>,
pub boot_type: BootType,
pub architecture: VMArchitecture,
/// VM allocated memory, in megabytes
pub memory: usize,
/// VM allocated RAM memory
pub memory: FileSize,
/// Number of vCPU for the VM
pub number_vcpu: usize,
/// Enable VNC access through admin console
@ -380,7 +380,7 @@ impl VMInfo {
memory: DomainMemoryXML {
unit: "MB".to_string(),
memory: self.memory,
memory: self.memory.as_mb(),
},
vcpu: DomainVCPUXML {
@ -452,7 +452,7 @@ impl VMInfo {
}
},
number_vcpu: domain.vcpu.body,
memory: convert_size_unit_to_mb(&domain.memory.unit, domain.memory.memory)?,
memory: FileSize::from_size_unit(&domain.memory.unit, domain.memory.memory)?,
vnc_access: domain.devices.graphics.is_some(),
iso_files: domain
.devices

View File

@ -1,3 +1,12 @@
use std::ops::Mul;
#[derive(thiserror::Error, Debug)]
enum FilesSizeUtilsError {
#[error("UnitConvertError: {0}")]
UnitConvert(String),
}
/// Holds a data size, convertible in any form
#[derive(
serde::Serialize,
serde::Deserialize,
@ -25,6 +34,30 @@ impl FileSize {
Self(gb * 1000 * 1000 * 1000)
}
/// Convert size unit to MB
pub fn from_size_unit(unit: &str, value: usize) -> anyhow::Result<Self> {
let fact = match unit {
"bytes" | "b" => 1f64,
"KB" => 1000f64,
"MB" => 1000f64 * 1000f64,
"GB" => 1000f64 * 1000f64 * 1000f64,
"TB" => 1000f64 * 1000f64 * 1000f64 * 1000f64,
"k" | "KiB" => 1024f64,
"M" | "MiB" => 1024f64 * 1024f64,
"G" | "GiB" => 1024f64 * 1024f64 * 1024f64,
"T" | "TiB" => 1024f64 * 1024f64 * 1024f64 * 1024f64,
_ => {
return Err(
FilesSizeUtilsError::UnitConvert(format!("Unknown size unit: {unit}")).into(),
);
}
};
Ok(Self((value as f64).mul(fact) as usize))
}
/// Get file size as bytes
pub fn as_bytes(&self) -> usize {
self.0
@ -35,3 +68,24 @@ impl FileSize {
self.0 / (1000 * 1000)
}
}
#[cfg(test)]
mod tests {
use crate::utils::file_size_utils::FileSize;
#[test]
fn convert_units_mb() {
assert_eq!(FileSize::from_size_unit("MB", 1).unwrap().as_mb(), 1);
assert_eq!(FileSize::from_size_unit("MB", 1000).unwrap().as_mb(), 1000);
assert_eq!(
FileSize::from_size_unit("GB", 1000).unwrap().as_mb(),
1000 * 1000
);
assert_eq!(FileSize::from_size_unit("GB", 1).unwrap().as_mb(), 1000);
assert_eq!(FileSize::from_size_unit("GiB", 3).unwrap().as_mb(), 3221);
assert_eq!(
FileSize::from_size_unit("KiB", 488281).unwrap().as_mb(),
499
);
}
}

View File

@ -1,13 +1,6 @@
use std::ops::{Div, Mul};
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
#[derive(thiserror::Error, Debug)]
enum FilesUtilsError {
#[error("UnitConvertError: {0}")]
UnitConvert(String),
}
const INVALID_CHARS: [&str; 19] = [
"@", "\\", "/", ":", ",", "<", ">", "%", "'", "\"", "?", "{", "}", "$", "*", "|", ";", "=",
"\t",
@ -35,31 +28,9 @@ pub fn set_file_permission<P: AsRef<Path>>(path: P, mode: u32) -> anyhow::Result
Ok(())
}
/// Convert size unit to MB
pub fn convert_size_unit_to_mb(unit: &str, value: usize) -> anyhow::Result<usize> {
let fact = match unit {
"bytes" | "b" => 1f64,
"KB" => 1000f64,
"MB" => 1000f64 * 1000f64,
"GB" => 1000f64 * 1000f64 * 1000f64,
"TB" => 1000f64 * 1000f64 * 1000f64 * 1000f64,
"k" | "KiB" => 1024f64,
"M" | "MiB" => 1024f64 * 1024f64,
"G" | "GiB" => 1024f64 * 1024f64 * 1024f64,
"T" | "TiB" => 1024f64 * 1024f64 * 1024f64 * 1024f64,
_ => {
return Err(FilesUtilsError::UnitConvert(format!("Unknown size unit: {unit}")).into());
}
};
Ok((value as f64).mul(fact.div((1000 * 1000) as f64)).ceil() as usize)
}
#[cfg(test)]
mod test {
use crate::utils::files_utils::{check_file_name, convert_size_unit_to_mb};
use crate::utils::files_utils::check_file_name;
#[test]
fn empty_file_name() {
@ -85,14 +56,4 @@ mod test {
fn valid_file_name() {
assert!(check_file_name("test.iso"));
}
#[test]
fn convert_units_mb() {
assert_eq!(convert_size_unit_to_mb("MB", 1).unwrap(), 1);
assert_eq!(convert_size_unit_to_mb("MB", 1000).unwrap(), 1000);
assert_eq!(convert_size_unit_to_mb("GB", 1000).unwrap(), 1000 * 1000);
assert_eq!(convert_size_unit_to_mb("GB", 1).unwrap(), 1000);
assert_eq!(convert_size_unit_to_mb("GiB", 3).unwrap(), 3222);
assert_eq!(convert_size_unit_to_mb("KiB", 488281).unwrap(), 500);
}
}

View File

@ -154,7 +154,7 @@ function VMListWidget(p: {
{row.name}
</TableCell>
<TableCell>{row.description ?? ""}</TableCell>
<TableCell>{vmMemoryToHuman(row.memory)}</TableCell>
<TableCell>{filesize(row.memory)}</TableCell>
<TableCell>{row.number_vcpu}</TableCell>
<TableCell>
<VMStatusWidget
@ -183,13 +183,13 @@ function VMListWidget(p: {
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell>
{vmMemoryToHuman(
{filesize(
p.list
.filter((v) => runningVMs.has(v.name))
.reduce((s, v) => s + v.memory, 0)
)}
{" / "}
{vmMemoryToHuman(p.list.reduce((s, v) => s + v.memory, 0))}
{filesize(p.list.reduce((s, v) => s + v.memory, 0))}
</TableCell>
<TableCell>
{p.list
@ -206,7 +206,3 @@ function VMListWidget(p: {
</TableContainer>
);
}
function vmMemoryToHuman(size: number): string {
return filesize(size * 1000 * 1000);
}

View File

@ -279,14 +279,16 @@ function VMDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement {
label="Memory (MB)"
editable={p.editable}
type="number"
value={p.vm.memory.toString()}
value={Math.floor(p.vm.memory / (1000 * 1000)).toString()}
onValueChange={(v) => {
p.vm.memory = Number(v ?? "0");
p.vm.memory = Number(v ?? "0") * 1000 * 1000;
p.onChange?.();
}}
checkValue={(v) =>
Number(v) > ServerApi.Config.constraints.memory_size.min &&
Number(v) < ServerApi.Config.constraints.memory_size.max
Number(v) >
ServerApi.Config.constraints.memory_size.min / (1000 * 1000) &&
Number(v) <
ServerApi.Config.constraints.memory_size.max / (1000 * 1000)
}
/>