Split file_disks_utils into two files
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
5814b0ab6a
commit
7451f1b7b4
@ -4,9 +4,9 @@ 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_disks_utils::{VMDiskFormat, VMFileDisk};
|
|
||||||
use crate::utils::files_utils;
|
use crate::utils::files_utils;
|
||||||
use crate::utils::files_utils::convert_size_unit_to_mb;
|
use crate::utils::files_utils::convert_size_unit_to_mb;
|
||||||
|
use crate::utils::vm_file_disks_utils::{VMDiskFormat, VMFileDisk};
|
||||||
use lazy_regex::regex;
|
use lazy_regex::regex;
|
||||||
use num::Integer;
|
use num::Integer;
|
||||||
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
use crate::app_config::AppConfig;
|
|
||||||
use crate::constants;
|
use crate::constants;
|
||||||
use crate::libvirt_lib_structures::XMLUuid;
|
|
||||||
use crate::utils::files_utils;
|
|
||||||
use lazy_regex::regex;
|
|
||||||
use std::os::linux::fs::MetadataExt;
|
use std::os::linux::fs::MetadataExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
@ -12,173 +8,6 @@ use std::time::UNIX_EPOCH;
|
|||||||
enum DisksError {
|
enum DisksError {
|
||||||
#[error("DiskParseError: {0}")]
|
#[error("DiskParseError: {0}")]
|
||||||
Parse(&'static str),
|
Parse(&'static str),
|
||||||
#[error("DiskConfigError: {0}")]
|
|
||||||
Config(&'static str),
|
|
||||||
#[error("DiskCreateError")]
|
|
||||||
Create,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type of disk allocation
|
|
||||||
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub enum VMDiskAllocType {
|
|
||||||
Fixed,
|
|
||||||
Sparse,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disk allocation type
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
|
||||||
#[serde(tag = "format")]
|
|
||||||
pub enum VMDiskFormat {
|
|
||||||
Raw {
|
|
||||||
/// Type of disk allocation
|
|
||||||
alloc_type: VMDiskAllocType,
|
|
||||||
},
|
|
||||||
QCow2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct VMFileDisk {
|
|
||||||
/// Disk name
|
|
||||||
pub name: String,
|
|
||||||
/// Disk size, in bytes
|
|
||||||
pub size: usize,
|
|
||||||
/// Disk format
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub format: VMDiskFormat,
|
|
||||||
/// Set this variable to true to delete the disk
|
|
||||||
pub delete: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VMFileDisk {
|
|
||||||
pub fn load_from_file(path: &str) -> anyhow::Result<Self> {
|
|
||||||
let file = Path::new(path);
|
|
||||||
|
|
||||||
let info = DiskFileInfo::load_file(file)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
name: info.name,
|
|
||||||
|
|
||||||
// Get only the virtual size of the file
|
|
||||||
size: match info.format {
|
|
||||||
DiskFileFormat::Raw { .. } => info.file_size,
|
|
||||||
DiskFileFormat::QCow2 { virtual_size } => virtual_size,
|
|
||||||
_ => anyhow::bail!("Unsupported image format: {:?}", info.format),
|
|
||||||
},
|
|
||||||
|
|
||||||
format: match info.format {
|
|
||||||
DiskFileFormat::Raw { is_sparse } => VMDiskFormat::Raw {
|
|
||||||
alloc_type: match is_sparse {
|
|
||||||
true => VMDiskAllocType::Sparse,
|
|
||||||
false => VMDiskAllocType::Fixed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
DiskFileFormat::QCow2 { .. } => VMDiskFormat::QCow2,
|
|
||||||
_ => anyhow::bail!("Unsupported image format: {:?}", info.format),
|
|
||||||
},
|
|
||||||
delete: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_config(&self) -> anyhow::Result<()> {
|
|
||||||
if constants::DISK_NAME_MIN_LEN > self.name.len()
|
|
||||||
|| constants::DISK_NAME_MAX_LEN < self.name.len()
|
|
||||||
{
|
|
||||||
return Err(DisksError::Config("Disk name length is invalid").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !regex!("^[a-zA-Z0-9]+$").is_match(&self.name) {
|
|
||||||
return Err(DisksError::Config("Disk name contains invalid characters!").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check disk size
|
|
||||||
if !(constants::DISK_SIZE_MIN..=constants::DISK_SIZE_MAX).contains(&self.size) {
|
|
||||||
return Err(DisksError::Config("Disk size is invalid!").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get disk path
|
|
||||||
pub fn disk_path(&self, id: XMLUuid) -> PathBuf {
|
|
||||||
let domain_dir = AppConfig::get().vm_storage_path(id);
|
|
||||||
let file_name = match self.format {
|
|
||||||
VMDiskFormat::Raw { .. } => self.name.to_string(),
|
|
||||||
VMDiskFormat::QCow2 => format!("{}.qcow2", self.name),
|
|
||||||
};
|
|
||||||
domain_dir.join(&file_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply disk configuration
|
|
||||||
pub fn apply_config(&self, id: XMLUuid) -> anyhow::Result<()> {
|
|
||||||
self.check_config()?;
|
|
||||||
|
|
||||||
let file = self.disk_path(id);
|
|
||||||
files_utils::create_directory_if_missing(file.parent().unwrap())?;
|
|
||||||
|
|
||||||
// Delete file if requested
|
|
||||||
if self.delete {
|
|
||||||
if !file.exists() {
|
|
||||||
log::debug!("File {file:?} does not exists, so it was not deleted");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info!("Deleting {file:?}");
|
|
||||||
std::fs::remove_file(file)?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if file.exists() {
|
|
||||||
log::debug!("File {file:?} does not exists, so it was not touched");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare command to create file
|
|
||||||
let res = match self.format {
|
|
||||||
VMDiskFormat::Raw { alloc_type } => {
|
|
||||||
let mut cmd = Command::new("/usr/bin/dd");
|
|
||||||
cmd.arg("if=/dev/zero")
|
|
||||||
.arg(format!("of={}", file.to_string_lossy()))
|
|
||||||
.arg("bs=1M");
|
|
||||||
|
|
||||||
match alloc_type {
|
|
||||||
VMDiskAllocType::Fixed => cmd.arg(format!("count={}", self.size_mb())),
|
|
||||||
VMDiskAllocType::Sparse => {
|
|
||||||
cmd.arg(format!("seek={}", self.size_mb())).arg("count=0")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
cmd.output()?
|
|
||||||
}
|
|
||||||
|
|
||||||
VMDiskFormat::QCow2 => {
|
|
||||||
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
|
||||||
cmd.arg("create")
|
|
||||||
.arg("-f")
|
|
||||||
.arg("qcow2")
|
|
||||||
.arg(file)
|
|
||||||
.arg(format!("{}M", self.size_mb()));
|
|
||||||
|
|
||||||
cmd.output()?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Execute Linux command
|
|
||||||
if !res.status.success() {
|
|
||||||
log::error!(
|
|
||||||
"Failed to create disk! stderr={} stdout={}",
|
|
||||||
String::from_utf8_lossy(&res.stderr),
|
|
||||||
String::from_utf8_lossy(&res.stdout)
|
|
||||||
);
|
|
||||||
return Err(DisksError::Create.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the size of file disk in megabytes
|
|
||||||
pub fn size_mb(&self) -> usize {
|
|
||||||
self.size / (1000 * 1000)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
@ -193,12 +22,12 @@ pub enum DiskFileFormat {
|
|||||||
/// Disk file information
|
/// Disk file information
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
pub struct DiskFileInfo {
|
pub struct DiskFileInfo {
|
||||||
file_size: usize,
|
pub file_size: usize,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
format: DiskFileFormat,
|
pub format: DiskFileFormat,
|
||||||
file_name: String,
|
pub file_name: String,
|
||||||
name: String,
|
pub name: String,
|
||||||
created: u64,
|
pub created: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiskFileInfo {
|
impl DiskFileInfo {
|
||||||
|
@ -5,3 +5,4 @@ pub mod net_utils;
|
|||||||
pub mod rand_utils;
|
pub mod rand_utils;
|
||||||
pub mod time_utils;
|
pub mod time_utils;
|
||||||
pub mod url_utils;
|
pub mod url_utils;
|
||||||
|
pub mod vm_file_disks_utils;
|
||||||
|
179
virtweb_backend/src/utils/vm_file_disks_utils.rs
Normal file
179
virtweb_backend/src/utils/vm_file_disks_utils.rs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
use crate::app_config::AppConfig;
|
||||||
|
use crate::constants;
|
||||||
|
use crate::libvirt_lib_structures::XMLUuid;
|
||||||
|
use crate::utils::file_disks_utils::{DiskFileFormat, DiskFileInfo};
|
||||||
|
use crate::utils::files_utils;
|
||||||
|
use lazy_regex::regex;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
enum VMDisksError {
|
||||||
|
#[error("DiskConfigError: {0}")]
|
||||||
|
Config(&'static str),
|
||||||
|
#[error("DiskCreateError")]
|
||||||
|
Create,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type of disk allocation
|
||||||
|
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum VMDiskAllocType {
|
||||||
|
Fixed,
|
||||||
|
Sparse,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disk allocation type
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(tag = "format")]
|
||||||
|
pub enum VMDiskFormat {
|
||||||
|
Raw {
|
||||||
|
/// Type of disk allocation
|
||||||
|
alloc_type: VMDiskAllocType,
|
||||||
|
},
|
||||||
|
QCow2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct VMFileDisk {
|
||||||
|
/// Disk name
|
||||||
|
pub name: String,
|
||||||
|
/// Disk size, in bytes
|
||||||
|
pub size: usize,
|
||||||
|
/// Disk format
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub format: VMDiskFormat,
|
||||||
|
/// Set this variable to true to delete the disk
|
||||||
|
pub delete: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VMFileDisk {
|
||||||
|
pub fn load_from_file(path: &str) -> anyhow::Result<Self> {
|
||||||
|
let file = Path::new(path);
|
||||||
|
|
||||||
|
let info = DiskFileInfo::load_file(file)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
name: info.name,
|
||||||
|
|
||||||
|
// Get only the virtual size of the file
|
||||||
|
size: match info.format {
|
||||||
|
DiskFileFormat::Raw { .. } => info.file_size,
|
||||||
|
DiskFileFormat::QCow2 { virtual_size } => virtual_size,
|
||||||
|
_ => anyhow::bail!("Unsupported image format: {:?}", info.format),
|
||||||
|
},
|
||||||
|
|
||||||
|
format: match info.format {
|
||||||
|
DiskFileFormat::Raw { is_sparse } => VMDiskFormat::Raw {
|
||||||
|
alloc_type: match is_sparse {
|
||||||
|
true => VMDiskAllocType::Sparse,
|
||||||
|
false => VMDiskAllocType::Fixed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DiskFileFormat::QCow2 { .. } => VMDiskFormat::QCow2,
|
||||||
|
_ => anyhow::bail!("Unsupported image format: {:?}", info.format),
|
||||||
|
},
|
||||||
|
delete: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_config(&self) -> anyhow::Result<()> {
|
||||||
|
if constants::DISK_NAME_MIN_LEN > self.name.len()
|
||||||
|
|| constants::DISK_NAME_MAX_LEN < self.name.len()
|
||||||
|
{
|
||||||
|
return Err(VMDisksError::Config("Disk name length is invalid").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !regex!("^[a-zA-Z0-9]+$").is_match(&self.name) {
|
||||||
|
return Err(VMDisksError::Config("Disk name contains invalid characters!").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check disk size
|
||||||
|
if !(constants::DISK_SIZE_MIN..=constants::DISK_SIZE_MAX).contains(&self.size) {
|
||||||
|
return Err(VMDisksError::Config("Disk size is invalid!").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get disk path
|
||||||
|
pub fn disk_path(&self, id: XMLUuid) -> PathBuf {
|
||||||
|
let domain_dir = AppConfig::get().vm_storage_path(id);
|
||||||
|
let file_name = match self.format {
|
||||||
|
VMDiskFormat::Raw { .. } => self.name.to_string(),
|
||||||
|
VMDiskFormat::QCow2 => format!("{}.qcow2", self.name),
|
||||||
|
};
|
||||||
|
domain_dir.join(&file_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply disk configuration
|
||||||
|
pub fn apply_config(&self, id: XMLUuid) -> anyhow::Result<()> {
|
||||||
|
self.check_config()?;
|
||||||
|
|
||||||
|
let file = self.disk_path(id);
|
||||||
|
files_utils::create_directory_if_missing(file.parent().unwrap())?;
|
||||||
|
|
||||||
|
// Delete file if requested
|
||||||
|
if self.delete {
|
||||||
|
if !file.exists() {
|
||||||
|
log::debug!("File {file:?} does not exists, so it was not deleted");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Deleting {file:?}");
|
||||||
|
std::fs::remove_file(file)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.exists() {
|
||||||
|
log::debug!("File {file:?} does not exists, so it was not touched");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare command to create file
|
||||||
|
let res = match self.format {
|
||||||
|
VMDiskFormat::Raw { alloc_type } => {
|
||||||
|
let mut cmd = Command::new("/usr/bin/dd");
|
||||||
|
cmd.arg("if=/dev/zero")
|
||||||
|
.arg(format!("of={}", file.to_string_lossy()))
|
||||||
|
.arg("bs=1M");
|
||||||
|
|
||||||
|
match alloc_type {
|
||||||
|
VMDiskAllocType::Fixed => cmd.arg(format!("count={}", self.size_mb())),
|
||||||
|
VMDiskAllocType::Sparse => {
|
||||||
|
cmd.arg(format!("seek={}", self.size_mb())).arg("count=0")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd.output()?
|
||||||
|
}
|
||||||
|
|
||||||
|
VMDiskFormat::QCow2 => {
|
||||||
|
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
||||||
|
cmd.arg("create")
|
||||||
|
.arg("-f")
|
||||||
|
.arg("qcow2")
|
||||||
|
.arg(file)
|
||||||
|
.arg(format!("{}M", self.size_mb()));
|
||||||
|
|
||||||
|
cmd.output()?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Execute Linux command
|
||||||
|
if !res.status.success() {
|
||||||
|
log::error!(
|
||||||
|
"Failed to create disk! stderr={} stdout={}",
|
||||||
|
String::from_utf8_lossy(&res.stderr),
|
||||||
|
String::from_utf8_lossy(&res.stdout)
|
||||||
|
);
|
||||||
|
return Err(VMDisksError::Create.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the size of file disk in megabytes
|
||||||
|
pub fn size_mb(&self) -> usize {
|
||||||
|
self.size / (1000 * 1000)
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,7 @@ docker compose up
|
|||||||
sudo mkdir /var/virtweb
|
sudo mkdir /var/virtweb
|
||||||
sudo chown $USER:$USER /var/virtweb
|
sudo chown $USER:$USER /var/virtweb
|
||||||
cd virtweb_backend
|
cd virtweb_backend
|
||||||
cargo fmt && cargo clippy && cargo run -- -s /var/virtweb --hypervisor-uri "qemu:///system"
|
cargo fmt && cargo clippy && cargo run -- -s /var/virtweb --hypervisor-uri "qemu:///system" --website-origin "http://localhost:5173"
|
||||||
```
|
```
|
||||||
|
|
||||||
7. Run the frontend
|
7. Run the frontend
|
||||||
|
Loading…
x
Reference in New Issue
Block a user