Generalize disk file creation logic
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
7451f1b7b4
commit
20de618568
@ -1,3 +1,5 @@
|
||||
use crate::utils::file_size_utils::FileSize;
|
||||
|
||||
/// Name of the cookie that contains session information
|
||||
pub const SESSION_COOKIE_NAME: &str = "X-auth-token";
|
||||
|
||||
@ -47,10 +49,10 @@ pub const DISK_NAME_MIN_LEN: usize = 2;
|
||||
pub const DISK_NAME_MAX_LEN: usize = 10;
|
||||
|
||||
/// Disk size min (B)
|
||||
pub const DISK_SIZE_MIN: usize = 100 * 1000 * 1000;
|
||||
pub const DISK_SIZE_MIN: FileSize = FileSize::from_mb(50);
|
||||
|
||||
/// Disk size max (B)
|
||||
pub const DISK_SIZE_MAX: usize = 1000 * 1000 * 1000 * 1000 * 2;
|
||||
pub const DISK_SIZE_MAX: FileSize = FileSize::from_gb(20000);
|
||||
|
||||
/// Net nat entry comment max size
|
||||
pub const NET_NAT_COMMENT_MAX_SIZE: usize = 250;
|
||||
|
@ -87,8 +87,8 @@ pub async fn static_config(local_auth: LocalAuthEnabled) -> impl Responder {
|
||||
max: DISK_NAME_MAX_LEN,
|
||||
},
|
||||
disk_size: LenConstraints {
|
||||
min: DISK_SIZE_MIN,
|
||||
max: DISK_SIZE_MAX,
|
||||
min: DISK_SIZE_MIN.as_bytes(),
|
||||
max: DISK_SIZE_MAX.as_bytes(),
|
||||
},
|
||||
|
||||
net_name_size: LenConstraints { min: 2, max: 50 },
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::constants;
|
||||
use crate::utils::file_size_utils::FileSize;
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
@ -8,13 +9,15 @@ use std::time::UNIX_EPOCH;
|
||||
enum DisksError {
|
||||
#[error("DiskParseError: {0}")]
|
||||
Parse(&'static str),
|
||||
#[error("DiskCreateError")]
|
||||
Create,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[serde(tag = "format")]
|
||||
pub enum DiskFileFormat {
|
||||
Raw { is_sparse: bool },
|
||||
QCow2 { virtual_size: usize },
|
||||
QCow2 { virtual_size: FileSize },
|
||||
CompressedRaw,
|
||||
CompressedQCow2,
|
||||
}
|
||||
@ -22,7 +25,7 @@ pub enum DiskFileFormat {
|
||||
/// Disk file information
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct DiskFileInfo {
|
||||
pub file_size: usize,
|
||||
pub file_size: FileSize,
|
||||
#[serde(flatten)]
|
||||
pub format: DiskFileFormat,
|
||||
pub file_name: String,
|
||||
@ -64,7 +67,7 @@ impl DiskFileInfo {
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
file_size: metadata.len() as usize,
|
||||
file_size: FileSize::from_bytes(metadata.len() as usize),
|
||||
format,
|
||||
file_name: file
|
||||
.file_name()
|
||||
@ -78,6 +81,50 @@ impl DiskFileInfo {
|
||||
.as_secs(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new empty disk
|
||||
pub fn create(file: &Path, format: DiskFileFormat, size: FileSize) -> anyhow::Result<()> {
|
||||
// Prepare command to create file
|
||||
let res = match format {
|
||||
DiskFileFormat::Raw { is_sparse } => {
|
||||
let mut cmd = Command::new("/usr/bin/dd");
|
||||
cmd.arg("if=/dev/zero")
|
||||
.arg(format!("of={}", file.to_string_lossy()))
|
||||
.arg("bs=1M");
|
||||
|
||||
match is_sparse {
|
||||
false => cmd.arg(format!("count={}", size.as_mb())),
|
||||
true => cmd.arg(format!("seek={}", size.as_mb())).arg("count=0"),
|
||||
};
|
||||
|
||||
cmd.output()?
|
||||
}
|
||||
|
||||
DiskFileFormat::QCow2 { virtual_size } => {
|
||||
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
||||
cmd.arg("create")
|
||||
.arg("-f")
|
||||
.arg("qcow2")
|
||||
.arg(file)
|
||||
.arg(format!("{}M", virtual_size.as_mb()));
|
||||
|
||||
cmd.output()?
|
||||
}
|
||||
_ => anyhow::bail!("Cannot create disk file image of this format: {format:?}!"),
|
||||
};
|
||||
|
||||
// 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(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
@ -87,7 +134,7 @@ struct QCowInfoOutput {
|
||||
}
|
||||
|
||||
/// Get QCow2 virtual size
|
||||
fn qcow_virt_size(path: &Path) -> anyhow::Result<usize> {
|
||||
fn qcow_virt_size(path: &Path) -> anyhow::Result<FileSize> {
|
||||
// Run qemu-img
|
||||
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
||||
cmd.args([
|
||||
@ -110,5 +157,5 @@ fn qcow_virt_size(path: &Path) -> anyhow::Result<usize> {
|
||||
|
||||
// Decode JSON
|
||||
let decoded: QCowInfoOutput = serde_json::from_str(&res_json)?;
|
||||
Ok(decoded.virtual_size)
|
||||
Ok(FileSize::from_bytes(decoded.virtual_size))
|
||||
}
|
||||
|
28
virtweb_backend/src/utils/file_size_utils.rs
Normal file
28
virtweb_backend/src/utils/file_size_utils.rs
Normal file
@ -0,0 +1,28 @@
|
||||
#[derive(
|
||||
serde::Serialize, serde::Deserialize, Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord,
|
||||
)]
|
||||
pub struct FileSize(usize);
|
||||
|
||||
impl FileSize {
|
||||
pub const fn from_bytes(size: usize) -> Self {
|
||||
Self(size)
|
||||
}
|
||||
|
||||
pub const fn from_mb(mb: usize) -> Self {
|
||||
Self(mb * 1000 * 1000)
|
||||
}
|
||||
|
||||
pub const fn from_gb(gb: usize) -> Self {
|
||||
Self(gb * 1000 * 1000 * 1000)
|
||||
}
|
||||
|
||||
/// Get file size as bytes
|
||||
pub fn as_bytes(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Get file size as megabytes
|
||||
pub fn as_mb(&self) -> usize {
|
||||
self.0 / (1000 * 1000)
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
pub mod exec_utils;
|
||||
pub mod file_disks_utils;
|
||||
pub mod file_size_utils;
|
||||
pub mod files_utils;
|
||||
pub mod net_utils;
|
||||
pub mod rand_utils;
|
||||
|
@ -2,21 +2,19 @@ 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::file_size_utils::FileSize;
|
||||
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)]
|
||||
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
|
||||
pub enum VMDiskAllocType {
|
||||
Fixed,
|
||||
Sparse,
|
||||
@ -38,7 +36,7 @@ pub struct VMFileDisk {
|
||||
/// Disk name
|
||||
pub name: String,
|
||||
/// Disk size, in bytes
|
||||
pub size: usize,
|
||||
pub size: FileSize,
|
||||
/// Disk format
|
||||
#[serde(flatten)]
|
||||
pub format: VMDiskFormat,
|
||||
@ -129,51 +127,20 @@ impl VMFileDisk {
|
||||
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());
|
||||
}
|
||||
// Create disk file
|
||||
DiskFileInfo::create(
|
||||
&file,
|
||||
match self.format {
|
||||
VMDiskFormat::Raw { alloc_type } => DiskFileFormat::Raw {
|
||||
is_sparse: alloc_type == VMDiskAllocType::Sparse,
|
||||
},
|
||||
VMDiskFormat::QCow2 => DiskFileFormat::QCow2 {
|
||||
virtual_size: self.size,
|
||||
},
|
||||
},
|
||||
self.size,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the size of file disk in megabytes
|
||||
pub fn size_mb(&self) -> usize {
|
||||
self.size / (1000 * 1000)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user