Created first disk
This commit is contained in:
		@@ -13,6 +13,8 @@ export interface ServerConstraints {
 | 
			
		||||
  name_size: LenConstraint;
 | 
			
		||||
  title_size: LenConstraint;
 | 
			
		||||
  memory_size: LenConstraint;
 | 
			
		||||
  disk_name_size: LenConstraint;
 | 
			
		||||
  disk_size: LenConstraint;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface LenConstraint {
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,18 @@ export type VMState =
 | 
			
		||||
  | "PowerManagementSuspended"
 | 
			
		||||
  | "Other";
 | 
			
		||||
 | 
			
		||||
export type DiskAllocType = "Sparse" | "Fixed";
 | 
			
		||||
 | 
			
		||||
export interface VMDisk {
 | 
			
		||||
  size: number;
 | 
			
		||||
  name: string;
 | 
			
		||||
  alloc_type: DiskAllocType;
 | 
			
		||||
  delete: boolean;
 | 
			
		||||
 | 
			
		||||
  // application attribute
 | 
			
		||||
  new?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface VMInfoInterface {
 | 
			
		||||
  name: string;
 | 
			
		||||
  uuid?: string;
 | 
			
		||||
@@ -28,6 +40,7 @@ interface VMInfoInterface {
 | 
			
		||||
  memory: number;
 | 
			
		||||
  vnc_access: boolean;
 | 
			
		||||
  iso_file?: string;
 | 
			
		||||
  disks: VMDisk[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class VMInfo implements VMInfoInterface {
 | 
			
		||||
@@ -41,6 +54,7 @@ export class VMInfo implements VMInfoInterface {
 | 
			
		||||
  memory: number;
 | 
			
		||||
  vnc_access: boolean;
 | 
			
		||||
  iso_file?: string;
 | 
			
		||||
  disks: VMDisk[];
 | 
			
		||||
 | 
			
		||||
  constructor(int: VMInfoInterface) {
 | 
			
		||||
    this.name = int.name;
 | 
			
		||||
@@ -53,6 +67,7 @@ export class VMInfo implements VMInfoInterface {
 | 
			
		||||
    this.memory = int.memory;
 | 
			
		||||
    this.vnc_access = int.vnc_access;
 | 
			
		||||
    this.iso_file = int.iso_file;
 | 
			
		||||
    this.disks = int.disks;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static NewEmpty(): VMInfo {
 | 
			
		||||
@@ -62,6 +77,7 @@ export class VMInfo implements VMInfoInterface {
 | 
			
		||||
      architecture: "x86_64",
 | 
			
		||||
      memory: 1024,
 | 
			
		||||
      vnc_access: true,
 | 
			
		||||
      disks: [],
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,19 @@ export function TextInput(p: {
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  if (((!p.editable && p.value) ?? "") === "") return <></>;
 | 
			
		||||
 | 
			
		||||
  let valueError = undefined;
 | 
			
		||||
  if (p.value && p.value.length > 0) {
 | 
			
		||||
    if (p.size?.min && p.type !== "number" && p.value.length < p.size.min)
 | 
			
		||||
      valueError = "Invalid value size";
 | 
			
		||||
    if (p.checkValue && !p.checkValue(p.value)) valueError = "Invalid value!";
 | 
			
		||||
    if (
 | 
			
		||||
      p.type === "number" &&
 | 
			
		||||
      p.size &&
 | 
			
		||||
      (Number(p.value) > p.size.max || Number(p.value) < p.size.min)
 | 
			
		||||
    )
 | 
			
		||||
      valueError = "Invalide size range!";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <TextField
 | 
			
		||||
      label={p.label}
 | 
			
		||||
@@ -39,13 +52,8 @@ export function TextInput(p: {
 | 
			
		||||
      multiline={p.multiline}
 | 
			
		||||
      minRows={p.minRows}
 | 
			
		||||
      maxRows={p.maxRows}
 | 
			
		||||
      error={
 | 
			
		||||
        (p.checkValue &&
 | 
			
		||||
          p.value &&
 | 
			
		||||
          p.value.length > 0 &&
 | 
			
		||||
          !p.checkValue(p.value)) ||
 | 
			
		||||
        false
 | 
			
		||||
      }
 | 
			
		||||
      error={valueError !== undefined}
 | 
			
		||||
      helperText={valueError}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										114
									
								
								virtweb_frontend/src/widgets/forms/VMDisksList.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								virtweb_frontend/src/widgets/forms/VMDisksList.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
import {
 | 
			
		||||
  Avatar,
 | 
			
		||||
  Button,
 | 
			
		||||
  ListItem,
 | 
			
		||||
  ListItemAvatar,
 | 
			
		||||
  ListItemText,
 | 
			
		||||
  Paper,
 | 
			
		||||
} from "@mui/material";
 | 
			
		||||
import { VMDisk, VMInfo } from "../../api/VMApi";
 | 
			
		||||
import { filesize } from "filesize";
 | 
			
		||||
import Icon from "@mdi/react";
 | 
			
		||||
import { mdiHarddisk } from "@mdi/js";
 | 
			
		||||
import { TextInput } from "./TextInput";
 | 
			
		||||
import { ServerApi } from "../../api/ServerApi";
 | 
			
		||||
import { SelectInput } from "./SelectInput";
 | 
			
		||||
 | 
			
		||||
export function VMDisksList(p: {
 | 
			
		||||
  vm: VMInfo;
 | 
			
		||||
  onChange?: () => void;
 | 
			
		||||
  editable: boolean;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const addNewDisk = () => {
 | 
			
		||||
    p.vm.disks.push({
 | 
			
		||||
      alloc_type: "Sparse",
 | 
			
		||||
      size: 10000,
 | 
			
		||||
      delete: false,
 | 
			
		||||
      name: `disk${p.vm.disks.length}`,
 | 
			
		||||
      new: true,
 | 
			
		||||
    });
 | 
			
		||||
    p.onChange?.();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      {/* disks list */}
 | 
			
		||||
      {p.vm.disks.map((d, num) => (
 | 
			
		||||
        <DiskInfo
 | 
			
		||||
          key={num}
 | 
			
		||||
          editable={p.editable}
 | 
			
		||||
          disk={d}
 | 
			
		||||
          onChange={p.onChange}
 | 
			
		||||
        />
 | 
			
		||||
      ))}
 | 
			
		||||
 | 
			
		||||
      {p.editable && <Button onClick={addNewDisk}>Add new disk</Button>}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function DiskInfo(p: {
 | 
			
		||||
  editable: boolean;
 | 
			
		||||
  disk: VMDisk;
 | 
			
		||||
  onChange?: () => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  if (!p.editable || !p.disk.new)
 | 
			
		||||
    return (
 | 
			
		||||
      <ListItem>
 | 
			
		||||
        <ListItemAvatar>
 | 
			
		||||
          <Avatar>
 | 
			
		||||
            <Icon path={mdiHarddisk} />
 | 
			
		||||
          </Avatar>
 | 
			
		||||
        </ListItemAvatar>
 | 
			
		||||
        <ListItemText
 | 
			
		||||
          primary={p.disk.name}
 | 
			
		||||
          secondary={`${filesize(p.disk.size * 1000 * 1000)} - ${
 | 
			
		||||
            p.disk.alloc_type
 | 
			
		||||
          }`}
 | 
			
		||||
        />
 | 
			
		||||
        {/* TODO delete disk if editable */}
 | 
			
		||||
      </ListItem>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Paper elevation={3} style={{ margin: "10px", padding: "10px" }}>
 | 
			
		||||
      <TextInput
 | 
			
		||||
        editable={true}
 | 
			
		||||
        label="Disk name"
 | 
			
		||||
        size={ServerApi.Config.constraints.disk_name_size}
 | 
			
		||||
        checkValue={(v) => /^[a-zA-Z0-9]+$/.test(v)}
 | 
			
		||||
        value={p.disk.name}
 | 
			
		||||
        onValueChange={(v) => {
 | 
			
		||||
          p.disk.name = v ?? "";
 | 
			
		||||
          p.onChange?.();
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <TextInput
 | 
			
		||||
        editable={true}
 | 
			
		||||
        label="Disk size (MB)"
 | 
			
		||||
        size={ServerApi.Config.constraints.disk_size}
 | 
			
		||||
        value={p.disk.size.toString()}
 | 
			
		||||
        onValueChange={(v) => {
 | 
			
		||||
          p.disk.size = Number(v ?? "0");
 | 
			
		||||
          p.onChange?.();
 | 
			
		||||
        }}
 | 
			
		||||
        type="number"
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <SelectInput
 | 
			
		||||
        editable={true}
 | 
			
		||||
        label="File allocation type"
 | 
			
		||||
        options={[
 | 
			
		||||
          { label: "Sparse allocation", value: "Sparse" },
 | 
			
		||||
          { label: "Fixed allocation", value: "Fixed" },
 | 
			
		||||
        ]}
 | 
			
		||||
        value={p.disk.alloc_type}
 | 
			
		||||
        onValueChange={(v) => {
 | 
			
		||||
          p.disk.alloc_type = v as any;
 | 
			
		||||
          p.onChange?.();
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
    </Paper>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@@ -11,6 +11,7 @@ import { IsoFile, IsoFilesApi } from "../../api/IsoFilesApi";
 | 
			
		||||
import { AsyncWidget } from "../AsyncWidget";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { filesize } from "filesize";
 | 
			
		||||
import { VMDisksList } from "../forms/VMDisksList";
 | 
			
		||||
 | 
			
		||||
interface DetailsProps {
 | 
			
		||||
  vm: VMInfo;
 | 
			
		||||
@@ -174,6 +175,7 @@ function VMDetailsInner(
 | 
			
		||||
            }),
 | 
			
		||||
          ]}
 | 
			
		||||
        />
 | 
			
		||||
        <VMDisksList vm={p.vm} editable={p.editable} onChange={p.onChange} />
 | 
			
		||||
      </EditSection>
 | 
			
		||||
    </Grid>
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user