Can configure network autostart
This commit is contained in:
parent
23f2029deb
commit
8b53875349
@ -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 */}
|
||||||
|
Loading…
Reference in New Issue
Block a user