diff --git a/virtweb_backend/src/actors/libvirt_actor.rs b/virtweb_backend/src/actors/libvirt_actor.rs index ccb9b03..288d33f 100644 --- a/virtweb_backend/src/actors/libvirt_actor.rs +++ b/virtweb_backend/src/actors/libvirt_actor.rs @@ -182,6 +182,13 @@ impl Handler for LibVirtActor { false => sys::VIR_DOMAIN_UNDEFINE_NVRAM, })?; + // Delete associated cloud init disk + let cloud_init_disk = AppConfig::get().cloud_init_disk_path_for_vm(&domain_name); + if cloud_init_disk.exists() { + std::fs::remove_file(cloud_init_disk)?; + } + + // If requested, delete block storage associated with the VM if !msg.keep_files { log::info!("Delete storage associated with the domain"); let path = AppConfig::get().vm_storage_path(msg.id); diff --git a/virtweb_backend/src/app_config.rs b/virtweb_backend/src/app_config.rs index eb69af7..f270831 100644 --- a/virtweb_backend/src/app_config.rs +++ b/virtweb_backend/src/app_config.rs @@ -250,6 +250,19 @@ impl AppConfig { self.storage_path().join("iso") } + /// Get the path where generated cloud init disk image are stored + pub fn cloud_init_disk_storage_path(&self) -> PathBuf { + self.storage_path().join("cloud_init_disks") + } + + /// Get the path where the disk image of a VM is stored + pub fn cloud_init_disk_path_for_vm(&self, name: &str) -> PathBuf { + self.cloud_init_disk_storage_path().join(format!( + "{}-{name}.iso", + constants::CLOUD_INIT_IMAGE_PREFIX_NAME + )) + } + /// Get disk images storage directory pub fn disk_images_storage_path(&self) -> PathBuf { self.storage_path().join("disk_images") diff --git a/virtweb_backend/src/constants.rs b/virtweb_backend/src/constants.rs index f1dfe65..e1079ba 100644 --- a/virtweb_backend/src/constants.rs +++ b/virtweb_backend/src/constants.rs @@ -57,6 +57,9 @@ pub const DISK_SIZE_MIN: FileSize = FileSize::from_mb(50); /// Disk size max (B) pub const DISK_SIZE_MAX: FileSize = FileSize::from_gb(20000); +/// Cloud init generated disk image prefix +pub const CLOUD_INIT_IMAGE_PREFIX_NAME: &str = "virtweb-cloudinit-autogen-image"; + /// Net nat entry comment max size pub const NET_NAT_COMMENT_MAX_SIZE: usize = 250; diff --git a/virtweb_backend/src/libvirt_rest_structures/vm.rs b/virtweb_backend/src/libvirt_rest_structures/vm.rs index 214c9bd..68f10f1 100644 --- a/virtweb_backend/src/libvirt_rest_structures/vm.rs +++ b/virtweb_backend/src/libvirt_rest_structures/vm.rs @@ -142,9 +142,22 @@ impl VMInfo { return Err(StructureExtraction("Invalid number of vCPU specified!").into()); } - let mut disks = vec![]; + let mut iso_absolute_files = vec![]; - // Add ISO files + // Process cloud init image + if self.cloud_init.attach_config { + let cloud_init_disk_path = AppConfig::get().cloud_init_disk_path_for_vm(&self.name); + + // Apply latest cloud init configuration + std::fs::write( + &cloud_init_disk_path, + self.cloud_init.generate_nocloud_disk()?, + )?; + + iso_absolute_files.push(cloud_init_disk_path); + } + + // Process uploaded ISO files for iso_file in &self.iso_files { if !files_utils::check_file_name(iso_file) { return Err(StructureExtraction("ISO filename is invalid!").into()); @@ -156,6 +169,13 @@ impl VMInfo { return Err(StructureExtraction("Specified ISO file does not exists!").into()); } + iso_absolute_files.push(path); + } + + let mut disks = vec![]; + + // Add ISO disk files + for iso_path in iso_absolute_files { disks.push(DiskXML { r#type: "file".to_string(), device: "cdrom".to_string(), @@ -165,7 +185,7 @@ impl VMInfo { cache: "none".to_string(), }, source: DiskSourceXML { - file: path.to_string_lossy().to_string(), + file: iso_path.to_string_lossy().to_string(), }, target: DiskTargetXML { dev: format!( @@ -182,6 +202,7 @@ impl VMInfo { }) } + // Configure VNC access, if requested let (vnc_graphics, vnc_video) = match self.vnc_access { true => ( Some(GraphicsXML { @@ -495,6 +516,7 @@ impl VMInfo { .iter() .filter(|d| d.device == "cdrom") .map(|d| d.source.file.rsplit_once('/').unwrap().1.to_string()) + .filter(|d| !d.starts_with(constants::CLOUD_INIT_IMAGE_PREFIX_NAME)) .collect(), file_disks: domain diff --git a/virtweb_backend/src/main.rs b/virtweb_backend/src/main.rs index 4e5ed94..8090766 100644 --- a/virtweb_backend/src/main.rs +++ b/virtweb_backend/src/main.rs @@ -61,6 +61,8 @@ async fn main() -> std::io::Result<()> { 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().cloud_init_disk_storage_path()) + .unwrap(); files_utils::create_directory_if_missing(AppConfig::get().disk_images_storage_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().vnc_sockets_path()).unwrap(); files_utils::set_file_permission(AppConfig::get().vnc_sockets_path(), 0o777).unwrap(); diff --git a/virtweb_backend/src/utils/cloud_init_utils.rs b/virtweb_backend/src/utils/cloud_init_utils.rs index 22c96fd..3821347 100644 --- a/virtweb_backend/src/utils/cloud_init_utils.rs +++ b/virtweb_backend/src/utils/cloud_init_utils.rs @@ -8,18 +8,18 @@ use std::process::Command; /// 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, + pub attach_config: bool, /// Main user data - user_data: String, + pub user_data: String, /// Instance ID, set in metadata file #[serde(skip_serializing_if = "Option::is_none")] - instance_id: Option, + pub instance_id: Option, /// Local hostname, set in metadata file #[serde(skip_serializing_if = "Option::is_none")] - local_hostname: Option, + pub local_hostname: Option, /// Network configuration #[serde(skip_serializing_if = "Option::is_none")] - network_configuration: Option, + pub network_configuration: Option, } impl CloudInitConfig {