Managed to insert an ISO file
This commit is contained in:
		@@ -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>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user