import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, } from "@mui/material"; import React from "react"; import { DiskImage, DiskImageApi, DiskImageFormat } from "../api/DiskImageApi"; import { ServerApi } from "../api/ServerApi"; import { VMFileDisk, VMInfo } from "../api/VMApi"; import { useAlert } from "../hooks/providers/AlertDialogProvider"; import { useLoadingMessage } from "../hooks/providers/LoadingMessageProvider"; import { FileDiskImageWidget } from "../widgets/FileDiskImageWidget"; import { CheckboxInput } from "../widgets/forms/CheckboxInput"; import { SelectInput } from "../widgets/forms/SelectInput"; import { TextInput } from "../widgets/forms/TextInput"; import { VMDiskFileWidget } from "../widgets/vms/VMDiskFileWidget"; export function ConvertDiskImageDialog( p: { onCancel: () => void; onFinished: () => void; } & ( | { backup?: false; image: DiskImage } | { backup: true; disk: VMFileDisk; vm: VMInfo } ) ): React.ReactElement { const alert = useAlert(); const loadingMessage = useLoadingMessage(); const [format, setFormat] = React.useState<DiskImageFormat>({ format: "QCow2", }); const origFilename = p.backup ? p.disk.name : p.image.file_name; const [filename, setFilename] = React.useState(origFilename + ".qcow2"); const handleFormatChange = (value?: string) => { setFormat({ format: value ?? ("QCow2" as any) }); if (value === "QCow2") setFilename(`${origFilename}.qcow2`); if (value === "CompressedQCow2") setFilename(`${origFilename}.qcow2.gz`); if (value === "Raw") { setFilename(`${origFilename}.raw`); // Check sparse checkbox by default setFormat({ format: "Raw", is_sparse: true }); } if (value === "CompressedRaw") setFilename(`${origFilename}.raw.gz`); }; const handleSubmit = async () => { try { loadingMessage.show( p.backup ? "Performing backup..." : "Converting image..." ); // Perform the conversion / backup operation if (p.backup) await DiskImageApi.BackupVMDisk(p.vm, p.disk, filename, format); else await DiskImageApi.Convert(p.image, filename, format); p.onFinished(); alert(p.backup ? "Backup successful!" : "Conversion successful!"); } catch (e) { console.error("Failed to perform backup/conversion!", e); alert( p.backup ? `Failed to perform backup! ${e}` : `Failed to convert image! ${e}` ); } finally { loadingMessage.hide(); } }; return ( <Dialog open onClose={p.onCancel}> <DialogTitle> {p.backup ? `Backup disk ${p.disk.name}` : "Convert disk image"} </DialogTitle> <DialogContent> <DialogContentText> Select the destination format for this image: </DialogContentText> {/* Show details of of the image */} {p.backup ? ( <VMDiskFileWidget {...p} /> ) : ( <FileDiskImageWidget {...p} /> )} {/* New image format */} <SelectInput editable label="Target format" value={format.format} onValueChange={handleFormatChange} options={[ { value: "QCow2" }, { value: "Raw" }, { value: "CompressedRaw" }, { value: "CompressedQCow2" }, ]} /> {/* Check for sparse file */} {format.format === "Raw" && ( <CheckboxInput editable label="Sparse file" checked={format.is_sparse} onValueChange={(c) => { setFormat({ format: "Raw", is_sparse: c }); }} /> )} {/* New image name */} <TextInput editable label="New image name" value={filename} onValueChange={(s) => { setFilename(s ?? ""); }} size={ServerApi.Config.constraints.disk_image_name_size} helperText="The image name shall contain the proper file extension for the selected target format" /> </DialogContent> <DialogActions> <Button onClick={p.onCancel}>Cancel</Button> <Button onClick={handleSubmit} autoFocus> {p.backup ? "Perform backup" : "Convert image"} </Button> </DialogActions> </Dialog> ); }