Can configure network autostart

This commit is contained in:
Pierre HUBERT 2023-12-06 19:10:25 +01:00
parent 23f2029deb
commit 8b53875349
7 changed files with 143 additions and 22 deletions

View File

@ -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
*/ */

View File

@ -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;

View 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;
}

View File

@ -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>

View File

@ -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) {

View File

@ -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

View File

@ -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 */}