Can get the list of uploaded disk images
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2025-05-27 21:17:16 +02:00
parent b55880b43c
commit 19ec9992be
3 changed files with 117 additions and 30 deletions

View File

@ -6,6 +6,7 @@ use lazy_regex::regex;
use std::os::linux::fs::MetadataExt;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::UNIX_EPOCH;
#[derive(thiserror::Error, Debug)]
enum DisksError {
@ -52,35 +53,28 @@ impl FileDisk {
pub fn load_from_file(path: &str) -> anyhow::Result<Self> {
let file = Path::new(path);
if !file.is_file() {
return Err(DisksError::Parse("Path is not a file!").into());
}
let metadata = file.metadata()?;
let name = file.file_stem().and_then(|s| s.to_str()).unwrap_or("disk");
let ext = file.extension().and_then(|s| s.to_str()).unwrap_or("raw");
// Approximate raw file estimation
let is_raw_sparse = metadata.len() / 512 >= metadata.st_blocks();
let format = match ext {
"qcow2" => DiskFormat::QCow2,
"raw" => DiskFormat::Raw {
alloc_type: match is_raw_sparse {
true => DiskAllocType::Sparse,
false => DiskAllocType::Fixed,
},
},
_ => anyhow::bail!("Unsupported disk extension: {ext}!"),
};
let info = DiskFileInfo::load_file(file)?;
Ok(Self {
name: name.to_string(),
size: match format {
DiskFormat::Raw { .. } => metadata.len() as usize / (1000 * 1000),
DiskFormat::QCow2 => qcow_virt_size(path)? / (1000 * 1000),
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 } => DiskFormat::Raw {
alloc_type: match is_sparse {
true => DiskAllocType::Sparse,
false => DiskAllocType::Fixed,
},
},
DiskFileFormat::QCow2 { .. } => DiskFormat::QCow2,
_ => anyhow::bail!("Unsupported image format: {:?}", info.format),
},
format,
delete: false,
})
}
@ -180,6 +174,74 @@ impl FileDisk {
}
}
#[derive(Debug, serde::Serialize)]
pub enum DiskFileFormat {
Raw { is_sparse: bool },
QCow2 { virtual_size: usize },
CompressedRaw,
CompressedQCow2,
}
/// Disk file information
#[derive(serde::Serialize)]
pub struct DiskFileInfo {
file_size: usize,
format: DiskFileFormat,
file_name: String,
name: String,
created: u64,
}
impl DiskFileInfo {
/// Get disk image file information
pub fn load_file(file: &Path) -> anyhow::Result<Self> {
if !file.is_file() {
return Err(DisksError::Parse("Path is not a file!").into());
}
// Get file metadata
let metadata = file.metadata()?;
let mut name = file
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("disk")
.to_string();
let ext = file.extension().and_then(|s| s.to_str()).unwrap_or("raw");
// Determine file format
let format = match ext {
"qcow2" => DiskFileFormat::QCow2 {
virtual_size: qcow_virt_size(file)? / (1000 * 1000),
},
"raw" => DiskFileFormat::Raw {
is_sparse: metadata.len() / 512 >= metadata.st_blocks(),
},
"gz" if name.ends_with(".qcow2") => {
name = name.strip_suffix(".qcow2").unwrap_or(&name).to_string();
DiskFileFormat::CompressedQCow2
}
"gz" => DiskFileFormat::CompressedRaw,
_ => anyhow::bail!("Unsupported disk extension: {ext}!"),
};
Ok(Self {
name,
file_size: metadata.len() as usize / (1000 * 1000),
format,
file_name: file
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("")
.to_string(),
created: metadata
.created()?
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
})
}
}
#[derive(serde::Deserialize)]
struct QCowInfoOutput {
#[serde(rename = "virtual-size")]
@ -187,10 +249,16 @@ struct QCowInfoOutput {
}
/// Get QCow2 virtual size
fn qcow_virt_size(path: &str) -> anyhow::Result<usize> {
fn qcow_virt_size(path: &Path) -> anyhow::Result<usize> {
// Run qemu-img
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
cmd.args(["info", path, "--output", "json", "--force-share"]);
cmd.args([
"info",
path.to_str().unwrap_or(""),
"--output",
"json",
"--force-share",
]);
let output = cmd.output()?;
if !output.status.success() {
anyhow::bail!(