Can configure network autostart
This commit is contained in:
		@@ -95,6 +95,29 @@ export class NetworkApi {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Check if autostart is enabled on a network
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static async IsAutostart(net: NetworkInfo): Promise<boolean> {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      await APIClient.exec({
 | 
				
			||||||
 | 
					        uri: `/network/${net.uuid}/autostart`,
 | 
				
			||||||
 | 
					        method: "GET",
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    ).data.autostart;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Set autostart status of a network
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static async SetAutostart(net: NetworkInfo, enabled: boolean): Promise<void> {
 | 
				
			||||||
 | 
					    await APIClient.exec({
 | 
				
			||||||
 | 
					      uri: `/network/${net.uuid}/autostart`,
 | 
				
			||||||
 | 
					      method: "PUT",
 | 
				
			||||||
 | 
					      jsonData: { autostart: enabled },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Update an existing network
 | 
					   * Update an existing network
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,10 +46,10 @@ interface VMInfoInterface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export class VMInfo implements VMInfoInterface {
 | 
					export class VMInfo implements VMInfoInterface {
 | 
				
			||||||
  name: string;
 | 
					  name: string;
 | 
				
			||||||
  uuid?: string | undefined;
 | 
					  uuid?: string;
 | 
				
			||||||
  genid?: string | undefined;
 | 
					  genid?: string;
 | 
				
			||||||
  title?: string | undefined;
 | 
					  title?: string;
 | 
				
			||||||
  description?: string | undefined;
 | 
					  description?: string;
 | 
				
			||||||
  boot_type: "UEFI" | "UEFISecureBoot";
 | 
					  boot_type: "UEFI" | "UEFISecureBoot";
 | 
				
			||||||
  architecture: "i686" | "x86_64";
 | 
					  architecture: "i686" | "x86_64";
 | 
				
			||||||
  memory: number;
 | 
					  memory: number;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										79
									
								
								virtweb_frontend/src/widgets/forms/IPInput.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								virtweb_frontend/src/widgets/forms/IPInput.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					import { TextInput } from "./TextInput";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function IPInput(p: {
 | 
				
			||||||
 | 
					  label: string;
 | 
				
			||||||
 | 
					  editable: boolean;
 | 
				
			||||||
 | 
					  value?: string;
 | 
				
			||||||
 | 
					  onValueChange?: (newVal: string | undefined) => void;
 | 
				
			||||||
 | 
					  version: 4 | 6;
 | 
				
			||||||
 | 
					}): React.ReactElement {
 | 
				
			||||||
 | 
					  const { onValueChange, ...props } = p;
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <TextInput
 | 
				
			||||||
 | 
					      onValueChange={(v) => {
 | 
				
			||||||
 | 
					        onValueChange?.(p.version === 4 ? sanitizeIpV4(v) : sanitizeIpV6(v));
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function sanitizeIpV4(s: string | undefined): string | undefined {
 | 
				
			||||||
 | 
					  if (s === "" || s === undefined) return s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let split = s.split(".");
 | 
				
			||||||
 | 
					  if (split.length > 4) split.splice(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let needAnotherIteration = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const res = split
 | 
				
			||||||
 | 
					    .map((v) => {
 | 
				
			||||||
 | 
					      if (v === "") return "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const num = Number(v);
 | 
				
			||||||
 | 
					      if (isNaN(num) || num < 0) return "0";
 | 
				
			||||||
 | 
					      if (num > 255) {
 | 
				
			||||||
 | 
					        needAnotherIteration = true;
 | 
				
			||||||
 | 
					        return v.slice(0, 2) + "." + v.slice(2);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return num.toString();
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .join(".");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return needAnotherIteration ? sanitizeIpV4(res) : res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function sanitizeIpV6(s: string | undefined): string | undefined {
 | 
				
			||||||
 | 
					  if (s === "" || s === undefined) return s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const split = s.split(":");
 | 
				
			||||||
 | 
					  if (split.length > 8) split.splice(8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let needAnotherIteration = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let res = split
 | 
				
			||||||
 | 
					    .map((e) => {
 | 
				
			||||||
 | 
					      if (e === "") return e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const num = parseInt(e, 16);
 | 
				
			||||||
 | 
					      if (isNaN(num)) return "0";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let s = num.toString(16);
 | 
				
			||||||
 | 
					      if (num > 0xffff) {
 | 
				
			||||||
 | 
					        needAnotherIteration = true;
 | 
				
			||||||
 | 
					        return s.slice(0, 4) + ":" + s.slice(4);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return s;
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .join(":");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const firstIndex = res.indexOf("::");
 | 
				
			||||||
 | 
					  let nextIndex = res.lastIndexOf("::");
 | 
				
			||||||
 | 
					  while (nextIndex !== firstIndex) {
 | 
				
			||||||
 | 
					    res = res.slice(0, nextIndex) + res.slice(nextIndex + 1);
 | 
				
			||||||
 | 
					    nextIndex = res.lastIndexOf("::");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return needAnotherIteration ? sanitizeIpV6(res) : res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,14 +1,15 @@
 | 
				
			|||||||
import { Alert, CircularProgress, Typography } from "@mui/material";
 | 
					import { Alert, CircularProgress, Typography } from "@mui/material";
 | 
				
			||||||
import { VMApi, VMInfo } from "../../api/VMApi";
 | 
					 | 
				
			||||||
import { AsyncWidget } from "../AsyncWidget";
 | 
					import { AsyncWidget } from "../AsyncWidget";
 | 
				
			||||||
import React from "react";
 | 
					import React from "react";
 | 
				
			||||||
import { CheckboxInput } from "./CheckboxInput";
 | 
					import { CheckboxInput } from "./CheckboxInput";
 | 
				
			||||||
import { useAlert } from "../../hooks/providers/AlertDialogProvider";
 | 
					import { useAlert } from "../../hooks/providers/AlertDialogProvider";
 | 
				
			||||||
import { useSnackbar } from "../../hooks/providers/SnackbarProvider";
 | 
					import { useSnackbar } from "../../hooks/providers/SnackbarProvider";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function VMAutostartInput(p: {
 | 
					export function ResAutostartInput(p: {
 | 
				
			||||||
  editable: boolean;
 | 
					  editable: boolean;
 | 
				
			||||||
  vm: VMInfo;
 | 
					  checkAutotostart: () => Promise<boolean>;
 | 
				
			||||||
 | 
					  setAutotostart: (enable: boolean) => Promise<void>;
 | 
				
			||||||
 | 
					  ressourceName: string;
 | 
				
			||||||
}): React.ReactElement {
 | 
					}): React.ReactElement {
 | 
				
			||||||
  const alert = useAlert();
 | 
					  const alert = useAlert();
 | 
				
			||||||
  const snackbar = useSnackbar();
 | 
					  const snackbar = useSnackbar();
 | 
				
			||||||
@@ -16,25 +17,25 @@ export function VMAutostartInput(p: {
 | 
				
			|||||||
  const [enabled, setEnabled] = React.useState<boolean | undefined>();
 | 
					  const [enabled, setEnabled] = React.useState<boolean | undefined>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const load = async () => {
 | 
					  const load = async () => {
 | 
				
			||||||
    setEnabled(await VMApi.IsAutostart(p.vm));
 | 
					    setEnabled(await p.checkAutotostart());
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const update = async (enabled: boolean) => {
 | 
					  const update = async (enabled: boolean) => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await VMApi.SetAutostart(p.vm, enabled);
 | 
					      await p.setAutotostart(enabled);
 | 
				
			||||||
      snackbar("Autostart status successfully updated!");
 | 
					      snackbar(`Autostart status of ${p.ressourceName} successfully updated!`);
 | 
				
			||||||
      setEnabled(enabled);
 | 
					      setEnabled(enabled);
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      console.error(e);
 | 
					      console.error(e);
 | 
				
			||||||
      alert("Failed to update autostart status of the VM!");
 | 
					      alert(`Failed to update autostart status of ${p.ressourceName}!`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <AsyncWidget
 | 
					    <AsyncWidget
 | 
				
			||||||
      loadKey={p.vm.uuid}
 | 
					      loadKey={p.ressourceName}
 | 
				
			||||||
      load={load}
 | 
					      load={load}
 | 
				
			||||||
      errMsg="Failed to check autostart status of the VM!"
 | 
					      errMsg={`Failed to check autostart status of ${p.ressourceName}!`}
 | 
				
			||||||
      buildLoading={() => (
 | 
					      buildLoading={() => (
 | 
				
			||||||
        <Typography>
 | 
					        <Typography>
 | 
				
			||||||
          <CircularProgress size={"1rem"} /> Checking for autostart
 | 
					          <CircularProgress size={"1rem"} /> Checking for autostart
 | 
				
			||||||
@@ -42,27 +43,29 @@ export function VMAutostartInput(p: {
 | 
				
			|||||||
      )}
 | 
					      )}
 | 
				
			||||||
      buildError={(e: string) => <Alert severity="error">{e}</Alert>}
 | 
					      buildError={(e: string) => <Alert severity="error">{e}</Alert>}
 | 
				
			||||||
      build={() => (
 | 
					      build={() => (
 | 
				
			||||||
        <VMAutostartInputInner
 | 
					        <ResAutostartInputInner
 | 
				
			||||||
          editable={p.editable}
 | 
					          editable={p.editable}
 | 
				
			||||||
          enabled={enabled!}
 | 
					          enabled={enabled!}
 | 
				
			||||||
          setEnabled={update}
 | 
					          setEnabled={update}
 | 
				
			||||||
 | 
					          resName={p.ressourceName}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
      )}
 | 
					      )}
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function VMAutostartInputInner(p: {
 | 
					function ResAutostartInputInner(p: {
 | 
				
			||||||
  editable: boolean;
 | 
					  editable: boolean;
 | 
				
			||||||
  enabled: boolean;
 | 
					  enabled: boolean;
 | 
				
			||||||
  setEnabled: (b: boolean) => void;
 | 
					  setEnabled: (b: boolean) => void;
 | 
				
			||||||
 | 
					  resName: string;
 | 
				
			||||||
}): React.ReactElement {
 | 
					}): React.ReactElement {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <CheckboxInput
 | 
					      <CheckboxInput
 | 
				
			||||||
        editable={p.editable}
 | 
					        editable={p.editable}
 | 
				
			||||||
        checked={p.enabled}
 | 
					        checked={p.enabled}
 | 
				
			||||||
        label="Autostart VM"
 | 
					        label={`Autostart ${p.resName}`}
 | 
				
			||||||
        onValueChange={p.setEnabled}
 | 
					        onValueChange={p.setEnabled}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
@@ -16,7 +16,7 @@ export function TextInput(p: {
 | 
				
			|||||||
  maxRows?: number;
 | 
					  maxRows?: number;
 | 
				
			||||||
  type?: React.HTMLInputTypeAttribute;
 | 
					  type?: React.HTMLInputTypeAttribute;
 | 
				
			||||||
}): React.ReactElement {
 | 
					}): React.ReactElement {
 | 
				
			||||||
  if (((!p.editable && p.value) ?? "") === "") return <></>;
 | 
					  if (!p.editable && (p.value ?? "") === "") return <></>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let valueError = undefined;
 | 
					  let valueError = undefined;
 | 
				
			||||||
  if (p.value && p.value.length > 0) {
 | 
					  if (p.value && p.value.length > 0) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import { Checkbox, Grid } from "@mui/material";
 | 
					import { Checkbox, Grid } from "@mui/material";
 | 
				
			||||||
import React from "react";
 | 
					import React from "react";
 | 
				
			||||||
import { IpConfig, NetworkInfo } from "../../api/NetworksApi";
 | 
					import { IpConfig, NetworkApi, NetworkInfo } from "../../api/NetworksApi";
 | 
				
			||||||
import { ServerApi } from "../../api/ServerApi";
 | 
					import { ServerApi } from "../../api/ServerApi";
 | 
				
			||||||
import { AsyncWidget } from "../AsyncWidget";
 | 
					import { AsyncWidget } from "../AsyncWidget";
 | 
				
			||||||
import { EditSection } from "../forms/EditSection";
 | 
					import { EditSection } from "../forms/EditSection";
 | 
				
			||||||
@@ -9,6 +9,7 @@ import { SelectInput } from "../forms/SelectInput";
 | 
				
			|||||||
import { TextInput } from "../forms/TextInput";
 | 
					import { TextInput } from "../forms/TextInput";
 | 
				
			||||||
import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
 | 
					import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
 | 
				
			||||||
import { CheckboxInput } from "../forms/CheckboxInput";
 | 
					import { CheckboxInput } from "../forms/CheckboxInput";
 | 
				
			||||||
 | 
					import { ResAutostartInput } from "../forms/ResAutostartInput";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface DetailsProps {
 | 
					interface DetailsProps {
 | 
				
			||||||
  net: NetworkInfo;
 | 
					  net: NetworkInfo;
 | 
				
			||||||
@@ -75,8 +76,16 @@ function NetworkDetailsInner(
 | 
				
			|||||||
          }}
 | 
					          }}
 | 
				
			||||||
          multiline={true}
 | 
					          multiline={true}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {p.net.uuid && (
 | 
				
			||||||
 | 
					          <ResAutostartInput
 | 
				
			||||||
 | 
					            editable={p.editable}
 | 
				
			||||||
 | 
					            ressourceName="Network"
 | 
				
			||||||
 | 
					            checkAutotostart={() => NetworkApi.IsAutostart(p.net)}
 | 
				
			||||||
 | 
					            setAutotostart={(e) => NetworkApi.SetAutostart(p.net, e)}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
      </EditSection>
 | 
					      </EditSection>
 | 
				
			||||||
      {/* TODO :  autostart */}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <EditSection title="General settings">
 | 
					      <EditSection title="General settings">
 | 
				
			||||||
        <SelectInput
 | 
					        <SelectInput
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,16 +3,16 @@ import React from "react";
 | 
				
			|||||||
import { validate as validateUUID } from "uuid";
 | 
					import { validate as validateUUID } from "uuid";
 | 
				
			||||||
import { IsoFile, IsoFilesApi } from "../../api/IsoFilesApi";
 | 
					import { IsoFile, IsoFilesApi } from "../../api/IsoFilesApi";
 | 
				
			||||||
import { ServerApi } from "../../api/ServerApi";
 | 
					import { ServerApi } from "../../api/ServerApi";
 | 
				
			||||||
import { VMInfo } from "../../api/VMApi";
 | 
					import { VMApi, VMInfo } from "../../api/VMApi";
 | 
				
			||||||
import { AsyncWidget } from "../AsyncWidget";
 | 
					import { AsyncWidget } from "../AsyncWidget";
 | 
				
			||||||
import { CheckboxInput } from "../forms/CheckboxInput";
 | 
					import { CheckboxInput } from "../forms/CheckboxInput";
 | 
				
			||||||
import { EditSection } from "../forms/EditSection";
 | 
					import { EditSection } from "../forms/EditSection";
 | 
				
			||||||
import { SelectInput } from "../forms/SelectInput";
 | 
					import { SelectInput } from "../forms/SelectInput";
 | 
				
			||||||
import { TextInput } from "../forms/TextInput";
 | 
					import { TextInput } from "../forms/TextInput";
 | 
				
			||||||
import { VMAutostartInput } from "../forms/VMAutostartInput";
 | 
					 | 
				
			||||||
import { VMDisksList } from "../forms/VMDisksList";
 | 
					import { VMDisksList } from "../forms/VMDisksList";
 | 
				
			||||||
import { VMSelectIsoInput } from "../forms/VMSelectIsoInput";
 | 
					import { VMSelectIsoInput } from "../forms/VMSelectIsoInput";
 | 
				
			||||||
import { VMScreenshot } from "./VMScreenshot";
 | 
					import { VMScreenshot } from "./VMScreenshot";
 | 
				
			||||||
 | 
					import { ResAutostartInput } from "../forms/ResAutostartInput";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface DetailsProps {
 | 
					interface DetailsProps {
 | 
				
			||||||
  vm: VMInfo;
 | 
					  vm: VMInfo;
 | 
				
			||||||
@@ -156,7 +156,14 @@ function VMDetailsInner(
 | 
				
			|||||||
          }}
 | 
					          }}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {p.vm.uuid && <VMAutostartInput editable={p.editable} vm={p.vm} />}
 | 
					        {p.vm.uuid && (
 | 
				
			||||||
 | 
					          <ResAutostartInput
 | 
				
			||||||
 | 
					            editable={p.editable}
 | 
				
			||||||
 | 
					            ressourceName="VM"
 | 
				
			||||||
 | 
					            checkAutotostart={() => VMApi.IsAutostart(p.vm)}
 | 
				
			||||||
 | 
					            setAutotostart={(e) => VMApi.SetAutostart(p.vm, e)}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
      </EditSection>
 | 
					      </EditSection>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      {/* Storage section */}
 | 
					      {/* Storage section */}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user