1 Commits

Author SHA1 Message Date
b430816f1e Update Rust crate actix-http to 3.11.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-06-06 00:24:34 +00:00
10 changed files with 30 additions and 72 deletions

View File

@@ -122,22 +122,19 @@ pub const API_TOKEN_DESCRIPTION_MAX_LENGTH: usize = 30;
pub const API_TOKEN_RIGHT_PATH_MAX_LENGTH: usize = 255; pub const API_TOKEN_RIGHT_PATH_MAX_LENGTH: usize = 255;
/// Qemu image program path /// Qemu image program path
pub const PROGRAM_QEMU_IMAGE: &str = "/usr/bin/qemu-img"; pub const QEMU_IMAGE_PROGRAM: &str = "/usr/bin/qemu-img";
/// IP program path /// IP program path
pub const PROGRAM_IP: &str = "/usr/sbin/ip"; pub const IP_PROGRAM: &str = "/usr/sbin/ip";
/// Copy program path /// Copy program path
pub const PROGRAM_COPY: &str = "/bin/cp"; pub const COPY_PROGRAM: &str = "/bin/cp";
/// Gzip program path /// Gzip program path
pub const PROGRAM_GZIP: &str = "/usr/bin/gzip"; pub const GZIP_PROGRAM: &str = "/usr/bin/gzip";
/// Bash program /// Bash program
pub const PROGRAM_BASH: &str = "/usr/bin/bash"; pub const BASH_PROGRAM: &str = "/usr/bin/bash";
/// DD program /// DD program
pub const PROGRAM_DD: &str = "/usr/bin/dd"; pub const DD_PROGRAM: &str = "/usr/bin/dd";
/// cloud-localds program
pub const PROGRAM_CLOUD_LOCALDS: &str = "/usr/bin/cloud-localds";

View File

@@ -1,5 +1,4 @@
use crate::libvirt_lib_structures::XMLUuid; use crate::libvirt_lib_structures::XMLUuid;
use crate::utils::cloud_init_utils::CloudInitConfig;
/// VirtWeb specific metadata /// VirtWeb specific metadata
#[derive(serde::Serialize, serde::Deserialize, Default, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize, Default, Debug, Clone)]
@@ -9,8 +8,6 @@ pub struct DomainMetadataVirtWebXML {
pub ns: String, pub ns: String,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<String>, pub group: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cloud_init: Option<CloudInitConfig>,
} }
/// Domain metadata /// Domain metadata

View File

@@ -4,7 +4,6 @@ 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::cloud_init_utils::CloudInitConfig;
use crate::utils::file_size_utils::FileSize; use crate::utils::file_size_utils::FileSize;
use crate::utils::files_utils; use crate::utils::files_utils;
use crate::utils::vm_file_disks_utils::{VMDiskBus, VMDiskFormat, VMFileDisk}; use crate::utils::vm_file_disks_utils::{VMDiskBus, VMDiskFormat, VMFileDisk};
@@ -95,9 +94,6 @@ pub struct VMInfo {
pub tpm_module: bool, pub tpm_module: bool,
/// Strings injected as OEM Strings in SMBios configuration /// Strings injected as OEM Strings in SMBios configuration
pub oem_strings: Vec<String>, pub oem_strings: Vec<String>,
/// Cloud init configuration
#[serde(default)]
pub cloud_init: CloudInitConfig,
} }
impl VMInfo { impl VMInfo {
@@ -344,7 +340,6 @@ impl VMInfo {
virtweb: DomainMetadataVirtWebXML { virtweb: DomainMetadataVirtWebXML {
ns: "https://virtweb.communiquons.org".to_string(), ns: "https://virtweb.communiquons.org".to_string(),
group: self.group.clone().map(|g| g.0), group: self.group.clone().map(|g| g.0),
cloud_init: Some(self.cloud_init.clone()),
}, },
}), }),
os: OSXML { os: OSXML {
@@ -587,13 +582,6 @@ impl VMInfo {
.and_then(|s| s.oem_strings) .and_then(|s| s.oem_strings)
.map(|s| s.entries.iter().map(|o| o.content.to_string()).collect()) .map(|s| s.entries.iter().map(|o| o.content.to_string()).collect())
.unwrap_or_default(), .unwrap_or_default(),
cloud_init: domain
.metadata
.clone()
.unwrap_or_default()
.virtweb
.cloud_init
.unwrap_or_default(),
}) })
} }
} }

View File

@@ -47,17 +47,13 @@ async fn main() -> std::io::Result<()> {
log::debug!("Checking for required programs"); log::debug!("Checking for required programs");
exec_utils::check_program( exec_utils::check_program(
constants::PROGRAM_QEMU_IMAGE, constants::QEMU_IMAGE_PROGRAM,
"QEMU disk image utility is required to manipulate QCow2 files!", "QEMU disk image utility is required to manipulate QCow2 files!",
); );
exec_utils::check_program( exec_utils::check_program(
constants::PROGRAM_IP, constants::IP_PROGRAM,
"ip is required to access bridges information!", "ip is required to access bridges information!",
); );
exec_utils::check_program(
constants::PROGRAM_CLOUD_LOCALDS,
"cloud-localds from package cloud-image-utils is required to build cloud-init images!",
);
log::debug!("Create required directory, if missing"); log::debug!("Create required directory, if missing");
files_utils::create_directory_if_missing(AppConfig::get().iso_storage_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().iso_storage_path()).unwrap();

View File

@@ -1,19 +0,0 @@
/// VM Cloud Init configuration
///
/// RedHat documentation: https://docs.redhat.com/fr/documentation/red_hat_enterprise_linux/9/html/configuring_and_managing_cloud-init_for_rhel_9/configuring-cloud-init_cloud-content
/// cloud-localds source code: https://github.com/canonical/cloud-utils/blob/main/bin/cloud-localds
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Default)]
pub struct CloudInitConfig {
attach_config: bool,
/// Main user data
user_data: String,
/// Instance ID, set in metadata file
#[serde(skip_serializing_if = "Option::is_none")]
instance_id: Option<String>,
/// Local hostname, set in metadata file
#[serde(skip_serializing_if = "Option::is_none")]
local_hostname: Option<String>,
/// Network configuration
#[serde(skip_serializing_if = "Option::is_none")]
network_configuration: Option<String>,
}

View File

@@ -124,7 +124,7 @@ impl DiskFileInfo {
} }
DiskFileFormat::QCow2 { virtual_size } => { DiskFileFormat::QCow2 { virtual_size } => {
let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE); let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
cmd.arg("create") cmd.arg("create")
.arg("-f") .arg("-f")
.arg("qcow2") .arg("qcow2")
@@ -161,7 +161,7 @@ impl DiskFileInfo {
let mut cmd = match (self.format, dest_format) { let mut cmd = match (self.format, dest_format) {
// Decompress QCow2 // Decompress QCow2
(DiskFileFormat::CompressedQCow2, DiskFileFormat::QCow2 { .. }) => { (DiskFileFormat::CompressedQCow2, DiskFileFormat::QCow2 { .. }) => {
let mut cmd = Command::new(constants::PROGRAM_GZIP); let mut cmd = Command::new(constants::GZIP_PROGRAM);
cmd.arg("--keep") cmd.arg("--keep")
.arg("--decompress") .arg("--decompress")
.arg("--to-stdout") .arg("--to-stdout")
@@ -172,7 +172,7 @@ impl DiskFileInfo {
// Compress QCow2 // Compress QCow2
(DiskFileFormat::QCow2 { .. }, DiskFileFormat::CompressedQCow2) => { (DiskFileFormat::QCow2 { .. }, DiskFileFormat::CompressedQCow2) => {
let mut cmd = Command::new(constants::PROGRAM_GZIP); let mut cmd = Command::new(constants::GZIP_PROGRAM);
cmd.arg("--keep") cmd.arg("--keep")
.arg("--to-stdout") .arg("--to-stdout")
.arg(&self.file_path) .arg(&self.file_path)
@@ -182,7 +182,7 @@ impl DiskFileInfo {
// Convert QCow2 to Raw file // Convert QCow2 to Raw file
(DiskFileFormat::QCow2 { .. }, DiskFileFormat::Raw { is_sparse }) => { (DiskFileFormat::QCow2 { .. }, DiskFileFormat::Raw { is_sparse }) => {
let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE); let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
cmd.arg("convert") cmd.arg("convert")
.arg("-f") .arg("-f")
.arg("qcow2") .arg("qcow2")
@@ -201,7 +201,7 @@ impl DiskFileInfo {
// Clone a QCow file, using qemu-image instead of cp might improve "sparsification" of // Clone a QCow file, using qemu-image instead of cp might improve "sparsification" of
// file // file
(DiskFileFormat::QCow2 { .. }, DiskFileFormat::QCow2 { .. }) => { (DiskFileFormat::QCow2 { .. }, DiskFileFormat::QCow2 { .. }) => {
let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE); let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
cmd.arg("convert") cmd.arg("convert")
.arg("-f") .arg("-f")
.arg("qcow2") .arg("qcow2")
@@ -214,7 +214,7 @@ impl DiskFileInfo {
// Convert Raw to QCow2 file // Convert Raw to QCow2 file
(DiskFileFormat::Raw { .. }, DiskFileFormat::QCow2 { .. }) => { (DiskFileFormat::Raw { .. }, DiskFileFormat::QCow2 { .. }) => {
let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE); let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
cmd.arg("convert") cmd.arg("convert")
.arg("-f") .arg("-f")
.arg("raw") .arg("raw")
@@ -228,7 +228,7 @@ impl DiskFileInfo {
// Render raw file non sparse // Render raw file non sparse
(DiskFileFormat::Raw { is_sparse: true }, DiskFileFormat::Raw { is_sparse: false }) => { (DiskFileFormat::Raw { is_sparse: true }, DiskFileFormat::Raw { is_sparse: false }) => {
let mut cmd = Command::new(constants::PROGRAM_COPY); let mut cmd = Command::new(constants::COPY_PROGRAM);
cmd.arg("--sparse=never") cmd.arg("--sparse=never")
.arg(&self.file_path) .arg(&self.file_path)
.arg(&temp_file); .arg(&temp_file);
@@ -237,7 +237,7 @@ impl DiskFileInfo {
// Render raw file sparse // Render raw file sparse
(DiskFileFormat::Raw { is_sparse: false }, DiskFileFormat::Raw { is_sparse: true }) => { (DiskFileFormat::Raw { is_sparse: false }, DiskFileFormat::Raw { is_sparse: true }) => {
let mut cmd = Command::new(constants::PROGRAM_DD); let mut cmd = Command::new(constants::DD_PROGRAM);
cmd.arg("conv=sparse") cmd.arg("conv=sparse")
.arg(format!("if={}", self.file_path.display())) .arg(format!("if={}", self.file_path.display()))
.arg(format!("of={}", temp_file.display())); .arg(format!("of={}", temp_file.display()));
@@ -246,7 +246,7 @@ impl DiskFileInfo {
// Compress Raw // Compress Raw
(DiskFileFormat::Raw { .. }, DiskFileFormat::CompressedRaw) => { (DiskFileFormat::Raw { .. }, DiskFileFormat::CompressedRaw) => {
let mut cmd = Command::new(constants::PROGRAM_GZIP); let mut cmd = Command::new(constants::GZIP_PROGRAM);
cmd.arg("--keep") cmd.arg("--keep")
.arg("--to-stdout") .arg("--to-stdout")
.arg(&self.file_path) .arg(&self.file_path)
@@ -256,7 +256,7 @@ impl DiskFileInfo {
// Decompress Raw to not sparse file // Decompress Raw to not sparse file
(DiskFileFormat::CompressedRaw, DiskFileFormat::Raw { is_sparse: false }) => { (DiskFileFormat::CompressedRaw, DiskFileFormat::Raw { is_sparse: false }) => {
let mut cmd = Command::new(constants::PROGRAM_GZIP); let mut cmd = Command::new(constants::GZIP_PROGRAM);
cmd.arg("--keep") cmd.arg("--keep")
.arg("--decompress") .arg("--decompress")
.arg("--to-stdout") .arg("--to-stdout")
@@ -268,12 +268,12 @@ impl DiskFileInfo {
// Decompress Raw to sparse file // Decompress Raw to sparse file
// https://benou.fr/www/ben/decompressing-sparse-files.html // https://benou.fr/www/ben/decompressing-sparse-files.html
(DiskFileFormat::CompressedRaw, DiskFileFormat::Raw { is_sparse: true }) => { (DiskFileFormat::CompressedRaw, DiskFileFormat::Raw { is_sparse: true }) => {
let mut cmd = Command::new(constants::PROGRAM_BASH); let mut cmd = Command::new(constants::BASH_PROGRAM);
cmd.arg("-c").arg(format!( cmd.arg("-c").arg(format!(
"{} -d -c {} | {} conv=sparse of={}", "{} -d -c {} | {} conv=sparse of={}",
constants::PROGRAM_GZIP, constants::GZIP_PROGRAM,
self.file_path.display(), self.file_path.display(),
constants::PROGRAM_DD, constants::DD_PROGRAM,
temp_file.display() temp_file.display()
)); ));
cmd cmd
@@ -281,7 +281,7 @@ impl DiskFileInfo {
// Dumb copy of file // Dumb copy of file
(a, b) if a == b => { (a, b) if a == b => {
let mut cmd = Command::new(constants::PROGRAM_COPY); let mut cmd = Command::new(constants::COPY_PROGRAM);
cmd.arg("--sparse=auto") cmd.arg("--sparse=auto")
.arg(&self.file_path) .arg(&self.file_path)
.arg(&temp_file); .arg(&temp_file);
@@ -341,7 +341,7 @@ struct QCowInfoOutput {
/// Get QCow2 virtual size /// Get QCow2 virtual size
fn qcow_virt_size(path: &Path) -> anyhow::Result<FileSize> { fn qcow_virt_size(path: &Path) -> anyhow::Result<FileSize> {
// Run qemu-img // Run qemu-img
let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE); let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
cmd.args([ cmd.args([
"info", "info",
path.to_str().unwrap_or(""), path.to_str().unwrap_or(""),
@@ -353,7 +353,7 @@ fn qcow_virt_size(path: &Path) -> anyhow::Result<FileSize> {
if !output.status.success() { if !output.status.success() {
anyhow::bail!( anyhow::bail!(
"{} info failed, status: {}, stderr: {}", "{} info failed, status: {}, stderr: {}",
constants::PROGRAM_QEMU_IMAGE, constants::QEMU_IMAGE_PROGRAM,
output.status, output.status,
String::from_utf8_lossy(&output.stderr) String::from_utf8_lossy(&output.stderr)
); );

View File

@@ -1,4 +1,3 @@
pub mod cloud_init_utils;
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 file_size_utils;

View File

@@ -145,13 +145,13 @@ struct IPBridgeInfo {
/// Get the list of bridge interfaces /// Get the list of bridge interfaces
pub fn bridges_list() -> anyhow::Result<Vec<String>> { pub fn bridges_list() -> anyhow::Result<Vec<String>> {
let mut cmd = Command::new(constants::PROGRAM_IP); let mut cmd = Command::new(constants::IP_PROGRAM);
cmd.args(["-json", "link", "show", "type", "bridge"]); cmd.args(["-json", "link", "show", "type", "bridge"]);
let output = cmd.output()?; let output = cmd.output()?;
if !output.status.success() { if !output.status.success() {
anyhow::bail!( anyhow::bail!(
"{} failed, status: {}, stderr: {}", "{} failed, status: {}, stderr: {}",
constants::PROGRAM_IP, constants::IP_PROGRAM,
output.status, output.status,
String::from_utf8_lossy(&output.stderr) String::from_utf8_lossy(&output.stderr)
); );

View File

@@ -5,9 +5,9 @@
sudo apt install libvirt-dev sudo apt install libvirt-dev
``` ```
2. Libvirt and cloud image utilities must also be installed: 2. Libvirt must also be installed:
```bash ```bash
sudo apt install qemu-kvm libvirt-daemon-system cloud-image-utils sudo apt install qemu-kvm libvirt-daemon-system
``` ```
3. Allow the current user to manage VMs: 3. Allow the current user to manage VMs:

View File

@@ -12,10 +12,10 @@ The release file will be available in `virtweb_backend/target/release/virtweb_ba
This is the only artifact that must be copied to the server. It is recommended to copy it to the `/usr/local/bin` directory. This is the only artifact that must be copied to the server. It is recommended to copy it to the `/usr/local/bin` directory.
## Install requirements ## Install requirements
In order to work properly, VirtWeb relies on `libvirt`, `qemu`, `kvm` and `cloud-localds`: In order to work properly, VirtWeb relies on `libvirt`, `qemu` and `kvm`:
```bash ```bash
sudo apt install qemu-kvm libvirt-daemon-system libvirt0 libvirt-clients libvirt-daemon bridge-utils cloud-image-utils sudo apt install qemu-kvm libvirt-daemon-system libvirt0 libvirt-clients libvirt-daemon bridge-utils
``` ```
## Dedicated user ## Dedicated user