175 lines
5.3 KiB
Rust
175 lines
5.3 KiB
Rust
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};
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
enum VMDisksError {
|
|
#[error("DiskConfigError: {0}")]
|
|
Config(&'static str),
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
pub enum VMDiskBus {
|
|
Virtio,
|
|
SATA,
|
|
}
|
|
|
|
/// Disk allocation type
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
#[serde(tag = "format")]
|
|
pub enum VMDiskFormat {
|
|
Raw {
|
|
/// Is raw file a sparse file?
|
|
is_sparse: bool,
|
|
},
|
|
QCow2,
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
pub struct VMFileDisk {
|
|
/// Disk name
|
|
pub name: String,
|
|
/// Disk size, in bytes
|
|
pub size: FileSize,
|
|
/// Disk bus
|
|
pub bus: VMDiskBus,
|
|
/// Disk format
|
|
#[serde(flatten)]
|
|
pub format: VMDiskFormat,
|
|
/// When creating a new disk, specify the disk image template to use
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub from_image: Option<String>,
|
|
/// Set this variable to true to delete the disk
|
|
pub delete: bool,
|
|
}
|
|
|
|
impl VMFileDisk {
|
|
pub fn load_from_file(path: &str, bus: &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 { is_sparse },
|
|
DiskFileFormat::QCow2 { .. } => VMDiskFormat::QCow2,
|
|
_ => anyhow::bail!("Unsupported image format: {:?}", info.format),
|
|
},
|
|
|
|
bus: match bus {
|
|
"virtio" => VMDiskBus::Virtio,
|
|
"sata" => VMDiskBus::SATA,
|
|
_ => anyhow::bail!("Unsupported disk bus type: {bus}"),
|
|
},
|
|
|
|
delete: false,
|
|
from_image: None,
|
|
})
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
// Check specified disk image template
|
|
if let Some(disk_image) = &self.from_image {
|
|
if !files_utils::check_file_name(disk_image) {
|
|
return Err(VMDisksError::Config("Disk image template name is not valid!").into());
|
|
}
|
|
|
|
if !AppConfig::get().disk_images_file_path(disk_image).is_file() {
|
|
return Err(
|
|
VMDisksError::Config("Specified disk image file does not exist!").into(),
|
|
);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Get disk path on file system
|
|
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(());
|
|
}
|
|
|
|
let format = match self.format {
|
|
VMDiskFormat::Raw { is_sparse } => DiskFileFormat::Raw { is_sparse },
|
|
VMDiskFormat::QCow2 => DiskFileFormat::QCow2 {
|
|
virtual_size: self.size,
|
|
},
|
|
};
|
|
|
|
// Create / Restore disk file
|
|
match &self.from_image {
|
|
// Create disk file
|
|
None => {
|
|
DiskFileInfo::create(&file, format, self.size)?;
|
|
}
|
|
|
|
// Restore disk image template
|
|
Some(disk_img) => {
|
|
let src_file =
|
|
DiskFileInfo::load_file(&AppConfig::get().disk_images_file_path(disk_img))?;
|
|
src_file.convert(&file, format)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|