Automatically generate cloud disk image when updating domains configuration
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		| @@ -182,6 +182,13 @@ impl Handler<DeleteDomainReq> 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); | ||||
|   | ||||
| @@ -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") | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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<String>, | ||||
|     pub instance_id: Option<String>, | ||||
|     /// Local hostname, set in metadata file | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     local_hostname: Option<String>, | ||||
|     pub local_hostname: Option<String>, | ||||
|     /// Network configuration | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     network_configuration: Option<String>, | ||||
|     pub network_configuration: Option<String>, | ||||
| } | ||||
|  | ||||
| impl CloudInitConfig { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user