Compare commits
	
		
			1 Commits
		
	
	
		
			47fa43dbbb
			...
			e8b9b5a563
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e8b9b5a563 | 
| @@ -122,22 +122,19 @@ pub const API_TOKEN_DESCRIPTION_MAX_LENGTH: usize = 30; | ||||
| pub const API_TOKEN_RIGHT_PATH_MAX_LENGTH: usize = 255; | ||||
|  | ||||
| /// 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 | ||||
| pub const PROGRAM_IP: &str = "/usr/sbin/ip"; | ||||
| pub const IP_PROGRAM: &str = "/usr/sbin/ip"; | ||||
|  | ||||
| /// Copy program path | ||||
| pub const PROGRAM_COPY: &str = "/bin/cp"; | ||||
| pub const COPY_PROGRAM: &str = "/bin/cp"; | ||||
|  | ||||
| /// Gzip program path | ||||
| pub const PROGRAM_GZIP: &str = "/usr/bin/gzip"; | ||||
| pub const GZIP_PROGRAM: &str = "/usr/bin/gzip"; | ||||
|  | ||||
| /// Bash program | ||||
| pub const PROGRAM_BASH: &str = "/usr/bin/bash"; | ||||
| pub const BASH_PROGRAM: &str = "/usr/bin/bash"; | ||||
|  | ||||
| /// DD program | ||||
| pub const PROGRAM_DD: &str = "/usr/bin/dd"; | ||||
|  | ||||
| /// cloud-localds program | ||||
| pub const PROGRAM_CLOUD_LOCALDS: &str = "/usr/bin/cloud-localds"; | ||||
| pub const DD_PROGRAM: &str = "/usr/bin/dd"; | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| use crate::libvirt_lib_structures::XMLUuid; | ||||
| use crate::utils::cloud_init_utils::CloudInitConfig; | ||||
|  | ||||
| /// VirtWeb specific metadata | ||||
| #[derive(serde::Serialize, serde::Deserialize, Default, Debug, Clone)] | ||||
| @@ -9,8 +8,6 @@ pub struct DomainMetadataVirtWebXML { | ||||
|     pub ns: String, | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub group: Option<String>, | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub cloud_init: Option<CloudInitConfig>, | ||||
| } | ||||
|  | ||||
| /// Domain metadata | ||||
|   | ||||
| @@ -4,7 +4,6 @@ use crate::libvirt_lib_structures::XMLUuid; | ||||
| use crate::libvirt_lib_structures::domain::*; | ||||
| use crate::libvirt_rest_structures::LibVirtStructError; | ||||
| use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction; | ||||
| use crate::utils::cloud_init_utils::CloudInitConfig; | ||||
| use crate::utils::file_size_utils::FileSize; | ||||
| use crate::utils::files_utils; | ||||
| use crate::utils::vm_file_disks_utils::{VMDiskBus, VMDiskFormat, VMFileDisk}; | ||||
| @@ -95,9 +94,6 @@ pub struct VMInfo { | ||||
|     pub tpm_module: bool, | ||||
|     /// Strings injected as OEM Strings in SMBios configuration | ||||
|     pub oem_strings: Vec<String>, | ||||
|     /// Cloud init configuration | ||||
|     #[serde(default)] | ||||
|     pub cloud_init: CloudInitConfig, | ||||
| } | ||||
|  | ||||
| impl VMInfo { | ||||
| @@ -344,7 +340,6 @@ impl VMInfo { | ||||
|                 virtweb: DomainMetadataVirtWebXML { | ||||
|                     ns: "https://virtweb.communiquons.org".to_string(), | ||||
|                     group: self.group.clone().map(|g| g.0), | ||||
|                     cloud_init: Some(self.cloud_init.clone()), | ||||
|                 }, | ||||
|             }), | ||||
|             os: OSXML { | ||||
| @@ -587,13 +582,6 @@ impl VMInfo { | ||||
|                 .and_then(|s| s.oem_strings) | ||||
|                 .map(|s| s.entries.iter().map(|o| o.content.to_string()).collect()) | ||||
|                 .unwrap_or_default(), | ||||
|             cloud_init: domain | ||||
|                 .metadata | ||||
|                 .clone() | ||||
|                 .unwrap_or_default() | ||||
|                 .virtweb | ||||
|                 .cloud_init | ||||
|                 .unwrap_or_default(), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -47,17 +47,13 @@ async fn main() -> std::io::Result<()> { | ||||
|  | ||||
|     log::debug!("Checking for required programs"); | ||||
|     exec_utils::check_program( | ||||
|         constants::PROGRAM_QEMU_IMAGE, | ||||
|         constants::QEMU_IMAGE_PROGRAM, | ||||
|         "QEMU disk image utility is required to manipulate QCow2 files!", | ||||
|     ); | ||||
|     exec_utils::check_program( | ||||
|         constants::PROGRAM_IP, | ||||
|         constants::IP_PROGRAM, | ||||
|         "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"); | ||||
|     files_utils::create_directory_if_missing(AppConfig::get().iso_storage_path()).unwrap(); | ||||
|   | ||||
| @@ -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>, | ||||
| } | ||||
| @@ -124,7 +124,7 @@ impl DiskFileInfo { | ||||
|             } | ||||
|  | ||||
|             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") | ||||
|                     .arg("-f") | ||||
|                     .arg("qcow2") | ||||
| @@ -161,7 +161,7 @@ impl DiskFileInfo { | ||||
|         let mut cmd = match (self.format, dest_format) { | ||||
|             // Decompress QCow2 | ||||
|             (DiskFileFormat::CompressedQCow2, DiskFileFormat::QCow2 { .. }) => { | ||||
|                 let mut cmd = Command::new(constants::PROGRAM_GZIP); | ||||
|                 let mut cmd = Command::new(constants::GZIP_PROGRAM); | ||||
|                 cmd.arg("--keep") | ||||
|                     .arg("--decompress") | ||||
|                     .arg("--to-stdout") | ||||
| @@ -172,7 +172,7 @@ impl DiskFileInfo { | ||||
|  | ||||
|             // Compress QCow2 | ||||
|             (DiskFileFormat::QCow2 { .. }, DiskFileFormat::CompressedQCow2) => { | ||||
|                 let mut cmd = Command::new(constants::PROGRAM_GZIP); | ||||
|                 let mut cmd = Command::new(constants::GZIP_PROGRAM); | ||||
|                 cmd.arg("--keep") | ||||
|                     .arg("--to-stdout") | ||||
|                     .arg(&self.file_path) | ||||
| @@ -182,7 +182,7 @@ impl DiskFileInfo { | ||||
|  | ||||
|             // Convert QCow2 to Raw file | ||||
|             (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") | ||||
|                     .arg("-f") | ||||
|                     .arg("qcow2") | ||||
| @@ -201,7 +201,7 @@ impl DiskFileInfo { | ||||
|             // Clone a QCow file, using qemu-image instead of cp might improve "sparsification" of | ||||
|             // file | ||||
|             (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") | ||||
|                     .arg("-f") | ||||
|                     .arg("qcow2") | ||||
| @@ -214,7 +214,7 @@ impl DiskFileInfo { | ||||
|  | ||||
|             // Convert Raw to QCow2 file | ||||
|             (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") | ||||
|                     .arg("-f") | ||||
|                     .arg("raw") | ||||
| @@ -228,7 +228,7 @@ impl DiskFileInfo { | ||||
|  | ||||
|             // Render raw file non sparse | ||||
|             (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") | ||||
|                     .arg(&self.file_path) | ||||
|                     .arg(&temp_file); | ||||
| @@ -237,7 +237,7 @@ impl DiskFileInfo { | ||||
|  | ||||
|             // Render raw file sparse | ||||
|             (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") | ||||
|                     .arg(format!("if={}", self.file_path.display())) | ||||
|                     .arg(format!("of={}", temp_file.display())); | ||||
| @@ -246,7 +246,7 @@ impl DiskFileInfo { | ||||
|  | ||||
|             // Compress Raw | ||||
|             (DiskFileFormat::Raw { .. }, DiskFileFormat::CompressedRaw) => { | ||||
|                 let mut cmd = Command::new(constants::PROGRAM_GZIP); | ||||
|                 let mut cmd = Command::new(constants::GZIP_PROGRAM); | ||||
|                 cmd.arg("--keep") | ||||
|                     .arg("--to-stdout") | ||||
|                     .arg(&self.file_path) | ||||
| @@ -256,7 +256,7 @@ impl DiskFileInfo { | ||||
|  | ||||
|             // Decompress Raw to not sparse file | ||||
|             (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") | ||||
|                     .arg("--decompress") | ||||
|                     .arg("--to-stdout") | ||||
| @@ -268,12 +268,12 @@ impl DiskFileInfo { | ||||
|             // Decompress Raw to sparse file | ||||
|             // https://benou.fr/www/ben/decompressing-sparse-files.html | ||||
|             (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!( | ||||
|                     "{} -d -c {} | {} conv=sparse of={}", | ||||
|                     constants::PROGRAM_GZIP, | ||||
|                     constants::GZIP_PROGRAM, | ||||
|                     self.file_path.display(), | ||||
|                     constants::PROGRAM_DD, | ||||
|                     constants::DD_PROGRAM, | ||||
|                     temp_file.display() | ||||
|                 )); | ||||
|                 cmd | ||||
| @@ -281,7 +281,7 @@ impl DiskFileInfo { | ||||
|  | ||||
|             // Dumb copy of file | ||||
|             (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") | ||||
|                     .arg(&self.file_path) | ||||
|                     .arg(&temp_file); | ||||
| @@ -341,7 +341,7 @@ struct QCowInfoOutput { | ||||
| /// Get QCow2 virtual size | ||||
| fn qcow_virt_size(path: &Path) -> anyhow::Result<FileSize> { | ||||
|     // Run qemu-img | ||||
|     let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE); | ||||
|     let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM); | ||||
|     cmd.args([ | ||||
|         "info", | ||||
|         path.to_str().unwrap_or(""), | ||||
| @@ -353,7 +353,7 @@ fn qcow_virt_size(path: &Path) -> anyhow::Result<FileSize> { | ||||
|     if !output.status.success() { | ||||
|         anyhow::bail!( | ||||
|             "{} info failed, status: {}, stderr: {}", | ||||
|             constants::PROGRAM_QEMU_IMAGE, | ||||
|             constants::QEMU_IMAGE_PROGRAM, | ||||
|             output.status, | ||||
|             String::from_utf8_lossy(&output.stderr) | ||||
|         ); | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| pub mod cloud_init_utils; | ||||
| pub mod exec_utils; | ||||
| pub mod file_disks_utils; | ||||
| pub mod file_size_utils; | ||||
|   | ||||
| @@ -145,13 +145,13 @@ struct IPBridgeInfo { | ||||
|  | ||||
| /// Get the list of bridge interfaces | ||||
| 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"]); | ||||
|     let output = cmd.output()?; | ||||
|     if !output.status.success() { | ||||
|         anyhow::bail!( | ||||
|             "{} failed, status: {}, stderr: {}", | ||||
|             constants::PROGRAM_IP, | ||||
|             constants::IP_PROGRAM, | ||||
|             output.status, | ||||
|             String::from_utf8_lossy(&output.stderr) | ||||
|         ); | ||||
|   | ||||
| @@ -5,9 +5,9 @@ | ||||
| sudo apt install libvirt-dev | ||||
| ``` | ||||
|  | ||||
| 2. Libvirt and cloud image utilities must also be installed: | ||||
| 2. Libvirt must also be installed: | ||||
| ```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: | ||||
|   | ||||
| @@ -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. | ||||
|  | ||||
| ## 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 | ||||
| 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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user