Managed to insert an ISO file
This commit is contained in:
parent
b007826710
commit
d93a2b857a
@ -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")]
|
||||||
|
@ -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()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user