Simplify RAM management
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		@@ -36,11 +36,11 @@ pub const ALLOWED_DISK_IMAGES_MIME_TYPES: [&str; 2] =
 | 
				
			|||||||
/// Disk image max size
 | 
					/// Disk image max size
 | 
				
			||||||
pub const DISK_IMAGE_MAX_SIZE: FileSize = FileSize::from_gb(10 * 1000);
 | 
					pub const DISK_IMAGE_MAX_SIZE: FileSize = FileSize::from_gb(10 * 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Min VM memory size (MB)
 | 
					/// Min VM memory size
 | 
				
			||||||
pub const MIN_VM_MEMORY: usize = 100;
 | 
					pub const MIN_VM_MEMORY: FileSize = FileSize::from_mb(100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Max VM memory size (MB)
 | 
					/// Max VM memory size
 | 
				
			||||||
pub const MAX_VM_MEMORY: usize = 64000;
 | 
					pub const MAX_VM_MEMORY: FileSize = FileSize::from_gb(64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Disk name min length
 | 
					/// Disk name min length
 | 
				
			||||||
pub const DISK_NAME_MIN_LEN: usize = 2;
 | 
					pub const DISK_NAME_MIN_LEN: usize = 2;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,8 +80,8 @@ pub async fn static_config(local_auth: LocalAuthEnabled) -> impl Responder {
 | 
				
			|||||||
            vm_title_size: LenConstraints { min: 0, max: 50 },
 | 
					            vm_title_size: LenConstraints { min: 0, max: 50 },
 | 
				
			||||||
            group_id_size: LenConstraints { min: 3, max: 50 },
 | 
					            group_id_size: LenConstraints { min: 3, max: 50 },
 | 
				
			||||||
            memory_size: LenConstraints {
 | 
					            memory_size: LenConstraints {
 | 
				
			||||||
                min: constants::MIN_VM_MEMORY,
 | 
					                min: constants::MIN_VM_MEMORY.as_bytes(),
 | 
				
			||||||
                max: constants::MAX_VM_MEMORY,
 | 
					                max: constants::MAX_VM_MEMORY.as_bytes(),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            disk_name_size: LenConstraints {
 | 
					            disk_name_size: LenConstraints {
 | 
				
			||||||
                min: DISK_NAME_MIN_LEN,
 | 
					                min: DISK_NAME_MIN_LEN,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,8 +4,8 @@ use crate::libvirt_lib_structures::XMLUuid;
 | 
				
			|||||||
use crate::libvirt_lib_structures::domain::*;
 | 
					use crate::libvirt_lib_structures::domain::*;
 | 
				
			||||||
use crate::libvirt_rest_structures::LibVirtStructError;
 | 
					use crate::libvirt_rest_structures::LibVirtStructError;
 | 
				
			||||||
use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
 | 
					use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
 | 
				
			||||||
 | 
					use crate::utils::file_size_utils::FileSize;
 | 
				
			||||||
use crate::utils::files_utils;
 | 
					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 crate::utils::vm_file_disks_utils::{VMDiskFormat, VMFileDisk};
 | 
				
			||||||
use lazy_regex::regex;
 | 
					use lazy_regex::regex;
 | 
				
			||||||
use num::Integer;
 | 
					use num::Integer;
 | 
				
			||||||
@@ -70,8 +70,8 @@ pub struct VMInfo {
 | 
				
			|||||||
    pub group: Option<VMGroupId>,
 | 
					    pub group: Option<VMGroupId>,
 | 
				
			||||||
    pub boot_type: BootType,
 | 
					    pub boot_type: BootType,
 | 
				
			||||||
    pub architecture: VMArchitecture,
 | 
					    pub architecture: VMArchitecture,
 | 
				
			||||||
    /// VM allocated memory, in megabytes
 | 
					    /// VM allocated RAM memory
 | 
				
			||||||
    pub memory: usize,
 | 
					    pub memory: FileSize,
 | 
				
			||||||
    /// Number of vCPU for the VM
 | 
					    /// Number of vCPU for the VM
 | 
				
			||||||
    pub number_vcpu: usize,
 | 
					    pub number_vcpu: usize,
 | 
				
			||||||
    /// Enable VNC access through admin console
 | 
					    /// Enable VNC access through admin console
 | 
				
			||||||
@@ -380,7 +380,7 @@ impl VMInfo {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            memory: DomainMemoryXML {
 | 
					            memory: DomainMemoryXML {
 | 
				
			||||||
                unit: "MB".to_string(),
 | 
					                unit: "MB".to_string(),
 | 
				
			||||||
                memory: self.memory,
 | 
					                memory: self.memory.as_mb(),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            vcpu: DomainVCPUXML {
 | 
					            vcpu: DomainVCPUXML {
 | 
				
			||||||
@@ -452,7 +452,7 @@ impl VMInfo {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            number_vcpu: domain.vcpu.body,
 | 
					            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(),
 | 
					            vnc_access: domain.devices.graphics.is_some(),
 | 
				
			||||||
            iso_files: domain
 | 
					            iso_files: domain
 | 
				
			||||||
                .devices
 | 
					                .devices
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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(
 | 
					#[derive(
 | 
				
			||||||
    serde::Serialize,
 | 
					    serde::Serialize,
 | 
				
			||||||
    serde::Deserialize,
 | 
					    serde::Deserialize,
 | 
				
			||||||
@@ -25,6 +34,30 @@ impl FileSize {
 | 
				
			|||||||
        Self(gb * 1000 * 1000 * 1000)
 | 
					        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
 | 
					    /// Get file size as bytes
 | 
				
			||||||
    pub fn as_bytes(&self) -> usize {
 | 
					    pub fn as_bytes(&self) -> usize {
 | 
				
			||||||
        self.0
 | 
					        self.0
 | 
				
			||||||
@@ -35,3 +68,24 @@ impl FileSize {
 | 
				
			|||||||
        self.0 / (1000 * 1000)
 | 
					        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
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,6 @@
 | 
				
			|||||||
use std::ops::{Div, Mul};
 | 
					 | 
				
			||||||
use std::os::unix::fs::PermissionsExt;
 | 
					use std::os::unix::fs::PermissionsExt;
 | 
				
			||||||
use std::path::Path;
 | 
					use std::path::Path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(thiserror::Error, Debug)]
 | 
					 | 
				
			||||||
enum FilesUtilsError {
 | 
					 | 
				
			||||||
    #[error("UnitConvertError: {0}")]
 | 
					 | 
				
			||||||
    UnitConvert(String),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const INVALID_CHARS: [&str; 19] = [
 | 
					const INVALID_CHARS: [&str; 19] = [
 | 
				
			||||||
    "@", "\\", "/", ":", ",", "<", ">", "%", "'", "\"", "?", "{", "}", "$", "*", "|", ";", "=",
 | 
					    "@", "\\", "/", ":", ",", "<", ">", "%", "'", "\"", "?", "{", "}", "$", "*", "|", ";", "=",
 | 
				
			||||||
    "\t",
 | 
					    "\t",
 | 
				
			||||||
@@ -35,31 +28,9 @@ pub fn set_file_permission<P: AsRef<Path>>(path: P, mode: u32) -> anyhow::Result
 | 
				
			|||||||
    Ok(())
 | 
					    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)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod 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]
 | 
					    #[test]
 | 
				
			||||||
    fn empty_file_name() {
 | 
					    fn empty_file_name() {
 | 
				
			||||||
@@ -85,14 +56,4 @@ mod test {
 | 
				
			|||||||
    fn valid_file_name() {
 | 
					    fn valid_file_name() {
 | 
				
			||||||
        assert!(check_file_name("test.iso"));
 | 
					        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);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -154,7 +154,7 @@ function VMListWidget(p: {
 | 
				
			|||||||
                        {row.name}
 | 
					                        {row.name}
 | 
				
			||||||
                      </TableCell>
 | 
					                      </TableCell>
 | 
				
			||||||
                      <TableCell>{row.description ?? ""}</TableCell>
 | 
					                      <TableCell>{row.description ?? ""}</TableCell>
 | 
				
			||||||
                      <TableCell>{vmMemoryToHuman(row.memory)}</TableCell>
 | 
					                      <TableCell>{filesize(row.memory)}</TableCell>
 | 
				
			||||||
                      <TableCell>{row.number_vcpu}</TableCell>
 | 
					                      <TableCell>{row.number_vcpu}</TableCell>
 | 
				
			||||||
                      <TableCell>
 | 
					                      <TableCell>
 | 
				
			||||||
                        <VMStatusWidget
 | 
					                        <VMStatusWidget
 | 
				
			||||||
@@ -183,13 +183,13 @@ function VMListWidget(p: {
 | 
				
			|||||||
            <TableCell></TableCell>
 | 
					            <TableCell></TableCell>
 | 
				
			||||||
            <TableCell></TableCell>
 | 
					            <TableCell></TableCell>
 | 
				
			||||||
            <TableCell>
 | 
					            <TableCell>
 | 
				
			||||||
              {vmMemoryToHuman(
 | 
					              {filesize(
 | 
				
			||||||
                p.list
 | 
					                p.list
 | 
				
			||||||
                  .filter((v) => runningVMs.has(v.name))
 | 
					                  .filter((v) => runningVMs.has(v.name))
 | 
				
			||||||
                  .reduce((s, v) => s + v.memory, 0)
 | 
					                  .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>
 | 
				
			||||||
            <TableCell>
 | 
					            <TableCell>
 | 
				
			||||||
              {p.list
 | 
					              {p.list
 | 
				
			||||||
@@ -206,7 +206,3 @@ function VMListWidget(p: {
 | 
				
			|||||||
    </TableContainer>
 | 
					    </TableContainer>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
function vmMemoryToHuman(size: number): string {
 | 
					 | 
				
			||||||
  return filesize(size * 1000 * 1000);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -279,14 +279,16 @@ function VMDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement {
 | 
				
			|||||||
          label="Memory (MB)"
 | 
					          label="Memory (MB)"
 | 
				
			||||||
          editable={p.editable}
 | 
					          editable={p.editable}
 | 
				
			||||||
          type="number"
 | 
					          type="number"
 | 
				
			||||||
          value={p.vm.memory.toString()}
 | 
					          value={Math.floor(p.vm.memory / (1000 * 1000)).toString()}
 | 
				
			||||||
          onValueChange={(v) => {
 | 
					          onValueChange={(v) => {
 | 
				
			||||||
            p.vm.memory = Number(v ?? "0");
 | 
					            p.vm.memory = Number(v ?? "0") * 1000 * 1000;
 | 
				
			||||||
            p.onChange?.();
 | 
					            p.onChange?.();
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
          checkValue={(v) =>
 | 
					          checkValue={(v) =>
 | 
				
			||||||
            Number(v) > ServerApi.Config.constraints.memory_size.min &&
 | 
					            Number(v) >
 | 
				
			||||||
            Number(v) < ServerApi.Config.constraints.memory_size.max
 | 
					              ServerApi.Config.constraints.memory_size.min / (1000 * 1000) &&
 | 
				
			||||||
 | 
					            Number(v) <
 | 
				
			||||||
 | 
					              ServerApi.Config.constraints.memory_size.max / (1000 * 1000)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user