149 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
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 === "GzCompressedQCow2") setFilename(`${origFilename}.qcow2.gz`);
 | 
						|
    if (value === "XzCompressedQCow2") setFilename(`${origFilename}.qcow2.xz`);
 | 
						|
    if (value === "Raw") {
 | 
						|
      setFilename(`${origFilename}.raw`);
 | 
						|
      // Check sparse checkbox by default
 | 
						|
      setFormat({ format: "Raw", is_sparse: true });
 | 
						|
    }
 | 
						|
    if (value === "GzCompressedRaw") setFilename(`${origFilename}.raw.gz`);
 | 
						|
    if (value === "XzCompressedRaw") setFilename(`${origFilename}.raw.xz`);
 | 
						|
  };
 | 
						|
 | 
						|
  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: "GzCompressedRaw" },
 | 
						|
            { value: "XzCompressedRaw" },
 | 
						|
            { value: "GzCompressedQCow2" },
 | 
						|
            { value: "XzCompressedQCow2" },
 | 
						|
          ]}
 | 
						|
        />
 | 
						|
 | 
						|
        {/* 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>
 | 
						|
  );
 | 
						|
}
 |