diff --git a/virtweb_backend/src/libvirt_rest_structures.rs b/virtweb_backend/src/libvirt_rest_structures.rs index eb098e5..82584be 100644 --- a/virtweb_backend/src/libvirt_rest_structures.rs +++ b/virtweb_backend/src/libvirt_rest_structures.rs @@ -87,8 +87,8 @@ pub struct VMInfo { pub number_vcpu: usize, /// Enable VNC access through admin console pub vnc_access: bool, - /// Attach an ISO file - pub iso_file: Option, + /// Attach ISO file(s) + pub iso_files: Vec, /// Storage - https://access.redhat.com/documentation/fr-fr/red_hat_enterprise_linux/6/html/virtualization_administration_guide/sect-virtualization-virtualized_block_devices-adding_storage_devices_to_guests#sect-Virtualization-Adding_storage_devices_to_guests-Adding_file_based_storage_to_a_guest pub disks: Vec, /// Network cards @@ -133,7 +133,7 @@ impl VMInfo { let mut disks = vec![]; - if let Some(iso_file) = &self.iso_file { + for iso_file in &self.iso_files { if !files_utils::check_file_name(iso_file) { return Err(StructureExtraction("ISO filename is invalid!").into()); } @@ -156,12 +156,15 @@ impl VMInfo { file: path.to_string_lossy().to_string(), }, target: DiskTargetXML { - dev: "hdc".to_string(), + dev: format!( + "hd{}", + ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"][disks.len()] + ), bus: "usb".to_string(), }, readonly: Some(DiskReadOnlyXML {}), boot: DiskBootXML { - order: "1".to_string(), + order: (disks.len() + 1).to_string(), }, address: None, }) @@ -182,7 +185,7 @@ impl VMInfo { // Check disks name for duplicates for disk in &self.disks { if self.disks.iter().filter(|d| d.name == disk.name).count() > 1 { - return Err(StructureExtraction("Two differents disks have the same name!").into()); + return Err(StructureExtraction("Two different disks have the same name!").into()); } } @@ -328,12 +331,13 @@ impl VMInfo { number_vcpu: domain.vcpu.body, memory: convert_to_mb(&domain.memory.unit, domain.memory.memory)?, vnc_access: domain.devices.graphics.is_some(), - iso_file: domain + iso_files: domain .devices .disks .iter() - .find(|d| d.device == "cdrom") - .map(|d| d.source.file.rsplit_once('/').unwrap().1.to_string()), + .filter(|d| d.device == "cdrom") + .map(|d| d.source.file.rsplit_once('/').unwrap().1.to_string()) + .collect(), disks: domain .devices diff --git a/virtweb_frontend/src/api/VMApi.ts b/virtweb_frontend/src/api/VMApi.ts index 4111af3..694407a 100644 --- a/virtweb_frontend/src/api/VMApi.ts +++ b/virtweb_frontend/src/api/VMApi.ts @@ -52,7 +52,7 @@ interface VMInfoInterface { memory: number; number_vcpu: number; vnc_access: boolean; - iso_file?: string; + iso_files: string[]; disks: VMDisk[]; networks: VMNetInterface[]; } @@ -68,7 +68,7 @@ export class VMInfo implements VMInfoInterface { number_vcpu: number; memory: number; vnc_access: boolean; - iso_file?: string; + iso_files: string[]; disks: VMDisk[]; networks: VMNetInterface[]; @@ -83,7 +83,7 @@ export class VMInfo implements VMInfoInterface { this.number_vcpu = int.number_vcpu; this.memory = int.memory; this.vnc_access = int.vnc_access; - this.iso_file = int.iso_file; + this.iso_files = int.iso_files; this.disks = int.disks; this.networks = int.networks; } @@ -96,6 +96,7 @@ export class VMInfo implements VMInfoInterface { memory: 1024, number_vcpu: 1, vnc_access: true, + iso_files: [], disks: [], networks: [], }); diff --git a/virtweb_frontend/src/widgets/forms/VMSelectIsoInput.tsx b/virtweb_frontend/src/widgets/forms/VMSelectIsoInput.tsx index f4d7228..e33553a 100644 --- a/virtweb_frontend/src/widgets/forms/VMSelectIsoInput.tsx +++ b/virtweb_frontend/src/widgets/forms/VMSelectIsoInput.tsx @@ -16,59 +16,68 @@ import Icon from "@mdi/react"; export function VMSelectIsoInput(p: { editable: boolean; isoList: IsoFile[]; - value?: string; - onChange: (newVal?: string) => void; + attachedISOs: string[]; + onChange: (newVal: string[]) => void; }): React.ReactElement { - if (!p.value && !p.editable) return <>; - - if (p.value) { - const iso = p.isoList.find((d) => d.filename === p.value); - return ( - { - p.onChange(undefined); - }} - > - - - - - ) - } - > - - - - - - - - ); - } + if (!p.attachedISOs && !p.editable) return <>; return ( - { - return { - label: `${i.filename} ${filesize(i.size)}`, - value: i.filename, - }; - }), - ]} - /> + <> + { + if (v) { + p.attachedISOs.push(v); + p.onChange(p.attachedISOs); + } + }} + options={[ + { label: "None", value: undefined }, + ...p.isoList.map((i) => { + return { + label: `${i.filename} ${filesize(i.size)}`, + value: i.filename, + }; + }), + ]} + /> + + {p.attachedISOs.map((isoName, num) => { + const iso = p.isoList.find((d) => d.filename === isoName); + return ( + { + p.attachedISOs.splice(num, 1); + p.onChange(p.attachedISOs); + }} + > + + + + + ) + } + > + + + + + + + + ); + })} + ); } diff --git a/virtweb_frontend/src/widgets/vms/VMDetails.tsx b/virtweb_frontend/src/widgets/vms/VMDetails.tsx index 1e218fd..475b80d 100644 --- a/virtweb_frontend/src/widgets/vms/VMDetails.tsx +++ b/virtweb_frontend/src/widgets/vms/VMDetails.tsx @@ -203,9 +203,9 @@ function VMDetailsInner( { - p.vm.iso_file = v; + p.vm.iso_files = v; p.onChange?.(); }} />