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
|
/// Name of the cookie that contains session information
|
||||||
pub const SESSION_COOKIE_NAME: &str = "X-auth-token";
|
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;
|
pub const DISK_NAME_MAX_LEN: usize = 10;
|
||||||
|
|
||||||
/// Disk size min (B)
|
/// 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)
|
/// 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
|
/// Net nat entry comment max size
|
||||||
pub const NET_NAT_COMMENT_MAX_SIZE: usize = 250;
|
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,
|
max: DISK_NAME_MAX_LEN,
|
||||||
},
|
},
|
||||||
disk_size: LenConstraints {
|
disk_size: LenConstraints {
|
||||||
min: DISK_SIZE_MIN,
|
min: DISK_SIZE_MIN.as_bytes(),
|
||||||
max: DISK_SIZE_MAX,
|
max: DISK_SIZE_MAX.as_bytes(),
|
||||||
},
|
},
|
||||||
|
|
||||||
net_name_size: LenConstraints { min: 2, max: 50 },
|
net_name_size: LenConstraints { min: 2, max: 50 },
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::constants;
|
use crate::constants;
|
||||||
|
use crate::utils::file_size_utils::FileSize;
|
||||||
use std::os::linux::fs::MetadataExt;
|
use std::os::linux::fs::MetadataExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@ -8,13 +9,15 @@ use std::time::UNIX_EPOCH;
|
|||||||
enum DisksError {
|
enum DisksError {
|
||||||
#[error("DiskParseError: {0}")]
|
#[error("DiskParseError: {0}")]
|
||||||
Parse(&'static str),
|
Parse(&'static str),
|
||||||
|
#[error("DiskCreateError")]
|
||||||
|
Create,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
#[serde(tag = "format")]
|
#[serde(tag = "format")]
|
||||||
pub enum DiskFileFormat {
|
pub enum DiskFileFormat {
|
||||||
Raw { is_sparse: bool },
|
Raw { is_sparse: bool },
|
||||||
QCow2 { virtual_size: usize },
|
QCow2 { virtual_size: FileSize },
|
||||||
CompressedRaw,
|
CompressedRaw,
|
||||||
CompressedQCow2,
|
CompressedQCow2,
|
||||||
}
|
}
|
||||||
@ -22,7 +25,7 @@ pub enum DiskFileFormat {
|
|||||||
/// Disk file information
|
/// Disk file information
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
pub struct DiskFileInfo {
|
pub struct DiskFileInfo {
|
||||||
pub file_size: usize,
|
pub file_size: FileSize,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub format: DiskFileFormat,
|
pub format: DiskFileFormat,
|
||||||
pub file_name: String,
|
pub file_name: String,
|
||||||
@ -64,7 +67,7 @@ impl DiskFileInfo {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name,
|
name,
|
||||||
file_size: metadata.len() as usize,
|
file_size: FileSize::from_bytes(metadata.len() as usize),
|
||||||
format,
|
format,
|
||||||
file_name: file
|
file_name: file
|
||||||
.file_name()
|
.file_name()
|
||||||
@ -78,6 +81,50 @@ impl DiskFileInfo {
|
|||||||
.as_secs(),
|
.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)]
|
#[derive(serde::Deserialize)]
|
||||||
@ -87,7 +134,7 @@ struct QCowInfoOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get QCow2 virtual size
|
/// 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
|
// Run qemu-img
|
||||||
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
||||||
cmd.args([
|
cmd.args([
|
||||||
@ -110,5 +157,5 @@ fn qcow_virt_size(path: &Path) -> anyhow::Result<usize> {
|
|||||||
|
|
||||||
// Decode JSON
|
// Decode JSON
|
||||||
let decoded: QCowInfoOutput = serde_json::from_str(&res_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 exec_utils;
|
||||||
pub mod file_disks_utils;
|
pub mod file_disks_utils;
|
||||||
|
pub mod file_size_utils;
|
||||||
pub mod files_utils;
|
pub mod files_utils;
|
||||||
pub mod net_utils;
|
pub mod net_utils;
|
||||||
pub mod rand_utils;
|
pub mod rand_utils;
|
||||||
|
@ -2,21 +2,19 @@ use crate::app_config::AppConfig;
|
|||||||
use crate::constants;
|
use crate::constants;
|
||||||
use crate::libvirt_lib_structures::XMLUuid;
|
use crate::libvirt_lib_structures::XMLUuid;
|
||||||
use crate::utils::file_disks_utils::{DiskFileFormat, DiskFileInfo};
|
use crate::utils::file_disks_utils::{DiskFileFormat, DiskFileInfo};
|
||||||
|
use crate::utils::file_size_utils::FileSize;
|
||||||
use crate::utils::files_utils;
|
use crate::utils::files_utils;
|
||||||
use lazy_regex::regex;
|
use lazy_regex::regex;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
enum VMDisksError {
|
enum VMDisksError {
|
||||||
#[error("DiskConfigError: {0}")]
|
#[error("DiskConfigError: {0}")]
|
||||||
Config(&'static str),
|
Config(&'static str),
|
||||||
#[error("DiskCreateError")]
|
|
||||||
Create,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type of disk allocation
|
/// 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 {
|
pub enum VMDiskAllocType {
|
||||||
Fixed,
|
Fixed,
|
||||||
Sparse,
|
Sparse,
|
||||||
@ -38,7 +36,7 @@ pub struct VMFileDisk {
|
|||||||
/// Disk name
|
/// Disk name
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Disk size, in bytes
|
/// Disk size, in bytes
|
||||||
pub size: usize,
|
pub size: FileSize,
|
||||||
/// Disk format
|
/// Disk format
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub format: VMDiskFormat,
|
pub format: VMDiskFormat,
|
||||||
@ -129,51 +127,20 @@ impl VMFileDisk {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare command to create file
|
// Create disk file
|
||||||
let res = match self.format {
|
DiskFileInfo::create(
|
||||||
VMDiskFormat::Raw { alloc_type } => {
|
&file,
|
||||||
let mut cmd = Command::new("/usr/bin/dd");
|
match self.format {
|
||||||
cmd.arg("if=/dev/zero")
|
VMDiskFormat::Raw { alloc_type } => DiskFileFormat::Raw {
|
||||||
.arg(format!("of={}", file.to_string_lossy()))
|
is_sparse: alloc_type == VMDiskAllocType::Sparse,
|
||||||
.arg("bs=1M");
|
},
|
||||||
|
VMDiskFormat::QCow2 => DiskFileFormat::QCow2 {
|
||||||
match alloc_type {
|
virtual_size: self.size,
|
||||||
VMDiskAllocType::Fixed => cmd.arg(format!("count={}", self.size_mb())),
|
},
|
||||||
VMDiskAllocType::Sparse => {
|
},
|
||||||
cmd.arg(format!("seek={}", self.size_mb())).arg("count=0")
|
self.size,
|
||||||
}
|
)?;
|
||||||
};
|
|
||||||
|
|
||||||
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(())
|
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