Managed to insert an ISO file

This commit is contained in:
Pierre HUBERT 2023-10-19 18:12:24 +02:00
parent b007826710
commit d93a2b857a
5 changed files with 180 additions and 6 deletions

View File

@ -60,8 +60,13 @@ pub struct ACPIXML {}
#[derive(serde::Serialize, serde::Deserialize)] #[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "devices")] #[serde(rename = "devices")]
pub struct DevicesXML { pub struct DevicesXML {
/// Graphics (used for VNC
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub graphics: Option<GraphicsXML>, pub graphics: Option<GraphicsXML>,
/// Disks (used for storage)
#[serde(default, rename = "disk")]
pub disks: Option<DiskXML>, // TODO : change to vec
} }
/// Screen information /// Screen information
@ -74,6 +79,74 @@ pub struct GraphicsXML {
pub socket: String, pub socket: String,
} }
/// Disk information
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "disk")]
pub struct DiskXML {
#[serde(rename(serialize = "@type"))]
pub r#type: String,
#[serde(rename(serialize = "@device"))]
pub r#device: String,
pub driver: DiskDriverXML,
pub source: DiskSourceXML,
pub target: DiskTargetXML,
pub readonly: DiskReadOnlyXML,
pub boot: DiskBootXML,
pub address: DiskAddressXML,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "driver")]
pub struct DiskDriverXML {
#[serde(rename(serialize = "@name"))]
pub name: String,
#[serde(rename(serialize = "@type"))]
pub r#type: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "source")]
pub struct DiskSourceXML {
#[serde(rename(serialize = "@file"))]
pub file: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "target")]
pub struct DiskTargetXML {
#[serde(rename(serialize = "@dev"))]
pub dev: String,
#[serde(rename(serialize = "@bus"))]
pub bus: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "readonly")]
pub struct DiskReadOnlyXML {}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "boot")]
pub struct DiskBootXML {
#[serde(rename(serialize = "@order"))]
pub order: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "address")]
pub struct DiskAddressXML {
#[serde(rename(serialize = "@type"))]
pub r#type: String,
#[serde(rename(serialize = "@controller"))]
pub r#controller: String,
#[serde(rename(serialize = "@bus"))]
pub r#bus: String,
#[serde(rename(serialize = "@target"))]
pub r#target: String,
#[serde(rename(serialize = "@unit"))]
pub r#unit: String,
}
/// Domain RAM information /// Domain RAM information
#[derive(serde::Serialize, serde::Deserialize)] #[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "memory")] #[serde(rename = "memory")]

View File

@ -1,10 +1,12 @@
use crate::app_config::AppConfig; use crate::app_config::AppConfig;
use crate::constants; use crate::constants;
use crate::libvirt_lib_structures::{ use crate::libvirt_lib_structures::{
DevicesXML, DomainMemoryXML, DomainXML, DomainXMLUuid, FeaturesXML, GraphicsXML, OSLoaderXML, DevicesXML, DiskAddressXML, DiskBootXML, DiskDriverXML, DiskReadOnlyXML, DiskSourceXML,
OSTypeXML, ACPIXML, OSXML, DiskTargetXML, DiskXML, DomainMemoryXML, DomainXML, DomainXMLUuid, FeaturesXML, GraphicsXML,
OSLoaderXML, OSTypeXML, ACPIXML, OSXML,
}; };
use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction; use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
use crate::utils::files_utils;
use lazy_regex::regex; use lazy_regex::regex;
use std::ops::{Div, Mul}; use std::ops::{Div, Mul};
@ -70,8 +72,9 @@ pub struct VMInfo {
pub memory: usize, pub memory: usize,
/// Enable VNC access through admin console /// Enable VNC access through admin console
pub vnc_access: bool, pub vnc_access: bool,
/// Attach an ISO file
pub iso_file: Option<String>,
// TODO : storage // TODO : storage
// TODO : iso
// TODO : autostart // TODO : autostart
// TODO : network interface // TODO : network interface
} }
@ -105,6 +108,47 @@ impl VMInfo {
return Err(StructureExtraction("VM memory is invalid!").into()); return Err(StructureExtraction("VM memory is invalid!").into());
} }
let mut disks = vec![];
if let Some(iso_file) = &self.iso_file {
if !files_utils::check_file_name(iso_file) {
return Err(StructureExtraction("ISO filename is invalid!").into());
}
let path = AppConfig::get().iso_storage_path().join(iso_file);
if !path.exists() {
return Err(StructureExtraction("Specified ISO file does not exists!").into());
}
disks.push(DiskXML {
r#type: "file".to_string(),
device: "cdrom".to_string(),
driver: DiskDriverXML {
name: "qemu".to_string(),
r#type: "raw".to_string(),
},
source: DiskSourceXML {
file: path.to_string_lossy().to_string(),
},
target: DiskTargetXML {
dev: "hdc".to_string(),
bus: "ide".to_string(),
},
readonly: DiskReadOnlyXML {},
boot: DiskBootXML {
order: "1".to_string(),
},
address: DiskAddressXML {
r#type: "drive".to_string(),
controller: "0".to_string(),
bus: "1".to_string(),
target: "0".to_string(),
unit: "0".to_string(),
},
})
}
let vnc_graphics = match self.vnc_access { let vnc_graphics = match self.vnc_access {
true => Some(GraphicsXML { true => Some(GraphicsXML {
r#type: "vnc".to_string(), r#type: "vnc".to_string(),
@ -147,6 +191,7 @@ impl VMInfo {
devices: DevicesXML { devices: DevicesXML {
graphics: vnc_graphics, graphics: vnc_graphics,
disks: disks.into_iter().next(),
}, },
memory: DomainMemoryXML { memory: DomainMemoryXML {
@ -187,6 +232,12 @@ impl VMInfo {
}, },
memory: convert_to_mb(&domain.memory.unit, domain.memory.memory)?, memory: convert_to_mb(&domain.memory.unit, domain.memory.memory)?,
vnc_access: domain.devices.graphics.is_some(), vnc_access: domain.devices.graphics.is_some(),
iso_file: domain
.devices
.disks
.iter()
.find(|d| d.device == "cdrom")
.map(|d| d.source.file.rsplit_once('/').unwrap().1.to_string()),
}) })
} }
} }

View File

@ -27,6 +27,7 @@ interface VMInfoInterface {
architecture: "i686" | "x86_64"; architecture: "i686" | "x86_64";
memory: number; memory: number;
vnc_access: boolean; vnc_access: boolean;
iso_file?: string;
} }
export class VMInfo implements VMInfoInterface { export class VMInfo implements VMInfoInterface {
@ -39,6 +40,7 @@ export class VMInfo implements VMInfoInterface {
architecture: "i686" | "x86_64"; architecture: "i686" | "x86_64";
memory: number; memory: number;
vnc_access: boolean; vnc_access: boolean;
iso_file?: string;
constructor(int: VMInfoInterface) { constructor(int: VMInfoInterface) {
this.name = int.name; this.name = int.name;
@ -50,6 +52,7 @@ export class VMInfo implements VMInfoInterface {
this.architecture = int.architecture; this.architecture = int.architecture;
this.memory = int.memory; this.memory = int.memory;
this.vnc_access = int.vnc_access; this.vnc_access = int.vnc_access;
this.iso_file = int.iso_file;
} }
static NewEmpty(): VMInfo { static NewEmpty(): VMInfo {

View File

@ -2,7 +2,7 @@ import { FormControl, InputLabel, MenuItem, Select } from "@mui/material";
import { TextInput } from "./TextInput"; import { TextInput } from "./TextInput";
export interface SelectOption { export interface SelectOption {
value: string; value?: string;
label: string; label: string;
} }

View File

@ -7,13 +7,38 @@ import { CheckboxInput } from "../forms/CheckboxInput";
import { SelectInput } from "../forms/SelectInput"; import { SelectInput } from "../forms/SelectInput";
import { TextInput } from "../forms/TextInput"; import { TextInput } from "../forms/TextInput";
import { VMScreenshot } from "./VMScreenshot"; import { VMScreenshot } from "./VMScreenshot";
import { IsoFile, IsoFilesApi } from "../../api/IsoFilesApi";
import { AsyncWidget } from "../AsyncWidget";
import React from "react";
import { filesize } from "filesize";
export function VMDetails(p: { interface DetailsProps {
vm: VMInfo; vm: VMInfo;
editable: boolean; editable: boolean;
onChange?: () => void; onChange?: () => void;
screenshot?: boolean; screenshot?: boolean;
}): React.ReactElement { }
export function VMDetails(p: DetailsProps): React.ReactElement {
const [list, setList] = React.useState<IsoFile[] | any>();
const load = async () => {
setList(await IsoFilesApi.GetList());
};
return (
<AsyncWidget
loadKey={"1"}
load={load}
errMsg="Failed to load the list of ISO files"
build={() => <VMDetailsInner iso={list!} {...p} />}
/>
);
}
function VMDetailsInner(
p: DetailsProps & { iso: IsoFile[] }
): React.ReactElement {
return ( return (
<Grid container spacing={2}> <Grid container spacing={2}>
{ {
@ -128,6 +153,28 @@ export function VMDetails(p: {
}} }}
/> />
</EditSection> </EditSection>
{/* Storage section */}
<EditSection title="Storage">
<SelectInput
label="ISO file"
editable={p.editable}
value={p.vm.iso_file}
onValueChange={(v) => {
p.vm.iso_file = v;
p.onChange?.();
}}
options={[
{ label: "None", value: undefined },
...p.iso.map((i) => {
return {
label: `${i.filename} ${filesize(i.size)}`,
value: i.filename,
};
}),
]}
/>
</EditSection>
</Grid> </Grid>
); );
} }