Can customize from UI Cloud init metadata
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2025-06-10 20:30:51 +02:00
parent 0de15af10e
commit feca07558e
3 changed files with 117 additions and 1 deletions

View File

@ -0,0 +1,102 @@
import RefreshIcon from "@mui/icons-material/Refresh";
import { Grid, IconButton, InputAdornment, Tooltip } from "@mui/material";
import { v4 as uuidv4 } from "uuid";
import { VMInfo } from "../../api/VMApi";
import { CheckboxInput } from "./CheckboxInput";
import { EditSection } from "./EditSection";
import { SelectInput } from "./SelectInput";
import { TextInput } from "./TextInput";
type CloudInitProps = {
vm: VMInfo;
onChange?: () => void;
editable: boolean;
};
export function CloudInitEditor(p: CloudInitProps): React.ReactElement {
return (
<>
<EditSection>
{/* Attach cloud init disk */}
<CheckboxInput
{...p}
label="Attach Cloud Init disk"
checked={p.vm.cloud_init.attach_config}
onValueChange={(v) => {
p.vm.cloud_init.attach_config = v;
p.onChange?.();
}}
/>
</EditSection>
<Grid container spacing={2}>
<CloudInitMetadata
{...p}
editable={p.editable && p.vm.cloud_init.attach_config}
/>
</Grid>
</>
);
}
function CloudInitMetadata(p: CloudInitProps): React.ReactElement {
// Regenerate instance id
const reGenerateInstanceId = () => {
p.vm.cloud_init.instance_id = uuidv4();
p.onChange?.();
};
return (
<EditSection title="Metadata">
{/* Instance ID */}
<TextInput
{...p}
label="Instance ID"
value={p.vm.cloud_init.instance_id}
onValueChange={(v) => {
p.vm.cloud_init.instance_id = v;
p.onChange?.();
}}
endAdornment={
p.editable ? (
<InputAdornment position="end">
<Tooltip title="Generate a new instance ID">
<IconButton onClick={reGenerateInstanceId}>
<RefreshIcon />
</IconButton>
</Tooltip>
</InputAdornment>
) : (
<></>
)
}
/>
{/* Instance hostname */}
<TextInput
{...p}
label="Local hostname"
value={p.vm.cloud_init.local_hostname}
onValueChange={(v) => {
p.vm.cloud_init.local_hostname = v;
p.onChange?.();
}}
/>
{/* Data source mode */}
<SelectInput
{...p}
label="Data source mode"
value={p.vm.cloud_init.dsmode}
onValueChange={(v) => {
p.vm.cloud_init.dsmode = v as any;
p.onChange?.();
}}
options={[
{ label: "None", value: undefined },
{ value: "Net" },
{ value: "Local" },
]}
/>
</EditSection>
);
}

View File

@ -18,6 +18,7 @@ export function TextInput(p: {
style?: React.CSSProperties; style?: React.CSSProperties;
helperText?: string; helperText?: string;
disabled?: boolean; disabled?: boolean;
endAdornment?: React.ReactNode;
}): React.ReactElement { }): React.ReactElement {
if (!p.editable && (p.value ?? "") === "") return <></>; if (!p.editable && (p.value ?? "") === "") return <></>;
@ -51,6 +52,7 @@ export function TextInput(p: {
input: { input: {
readOnly: !p.editable, readOnly: !p.editable,
type: p.type, type: p.type,
endAdornment: p.endAdornment,
}, },
}} }}
variant={"standard"} variant={"standard"}

View File

@ -5,6 +5,7 @@ import Grid from "@mui/material/Grid";
import React from "react"; import React from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { validate as validateUUID } from "uuid"; import { validate as validateUUID } from "uuid";
import { DiskImage, DiskImageApi } from "../../api/DiskImageApi";
import { GroupApi } from "../../api/GroupApi"; import { GroupApi } from "../../api/GroupApi";
import { IsoFile, IsoFilesApi } from "../../api/IsoFilesApi"; import { IsoFile, IsoFilesApi } from "../../api/IsoFilesApi";
import { NWFilter, NWFilterApi } from "../../api/NWFilterApi"; import { NWFilter, NWFilterApi } from "../../api/NWFilterApi";
@ -18,6 +19,7 @@ import { AsyncWidget } from "../AsyncWidget";
import { TabsWidget } from "../TabsWidget"; import { TabsWidget } from "../TabsWidget";
import { XMLAsyncWidget } from "../XMLWidget"; import { XMLAsyncWidget } from "../XMLWidget";
import { CheckboxInput } from "../forms/CheckboxInput"; import { CheckboxInput } from "../forms/CheckboxInput";
import { CloudInitEditor } from "../forms/CloudInitEditor";
import { EditSection } from "../forms/EditSection"; import { EditSection } from "../forms/EditSection";
import { OEMStringFormWidget } from "../forms/OEMStringFormWidget"; import { OEMStringFormWidget } from "../forms/OEMStringFormWidget";
import { ResAutostartInput } from "../forms/ResAutostartInput"; import { ResAutostartInput } from "../forms/ResAutostartInput";
@ -27,7 +29,6 @@ import { VMDisksList } from "../forms/VMDisksList";
import { VMNetworksList } from "../forms/VMNetworksList"; import { VMNetworksList } from "../forms/VMNetworksList";
import { VMSelectIsoInput } from "../forms/VMSelectIsoInput"; import { VMSelectIsoInput } from "../forms/VMSelectIsoInput";
import { VMScreenshot } from "./VMScreenshot"; import { VMScreenshot } from "./VMScreenshot";
import { DiskImage, DiskImageApi } from "../../api/DiskImageApi";
interface DetailsProps { interface DetailsProps {
vm: VMInfo; vm: VMInfo;
@ -89,6 +90,7 @@ enum VMTab {
General = 0, General = 0,
Storage, Storage,
Network, Network,
CloudInit,
Advanced, Advanced,
XML, XML,
Danger, Danger,
@ -116,6 +118,11 @@ function VMDetailsInner(p: DetailsInnerProps): React.ReactElement {
{ label: "General", value: VMTab.General, visible: true }, { label: "General", value: VMTab.General, visible: true },
{ label: "Storage", value: VMTab.Storage, visible: true }, { label: "Storage", value: VMTab.Storage, visible: true },
{ label: "Network", value: VMTab.Network, visible: true }, { label: "Network", value: VMTab.Network, visible: true },
{
label: "Cloud Init",
value: VMTab.CloudInit,
visible: p.editable || p.vm.cloud_init.attach_config,
},
{ label: "Avanced", value: VMTab.Advanced, visible: true }, { label: "Avanced", value: VMTab.Advanced, visible: true },
{ {
@ -135,6 +142,7 @@ function VMDetailsInner(p: DetailsInnerProps): React.ReactElement {
{currTab === VMTab.General && <VMDetailsTabGeneral {...p} />} {currTab === VMTab.General && <VMDetailsTabGeneral {...p} />}
{currTab === VMTab.Storage && <VMDetailsTabStorage {...p} />} {currTab === VMTab.Storage && <VMDetailsTabStorage {...p} />}
{currTab === VMTab.Network && <VMDetailsTabNetwork {...p} />} {currTab === VMTab.Network && <VMDetailsTabNetwork {...p} />}
{currTab === VMTab.CloudInit && <VMDetailsTabCloudInit {...p} />}
{currTab === VMTab.Advanced && <VMDetailsTabAdvanced {...p} />} {currTab === VMTab.Advanced && <VMDetailsTabAdvanced {...p} />}
{currTab === VMTab.XML && <VMDetailsTabXML {...p} />} {currTab === VMTab.XML && <VMDetailsTabXML {...p} />}
{currTab === VMTab.Danger && <VMDetailsTabDanger {...p} />} {currTab === VMTab.Danger && <VMDetailsTabDanger {...p} />}
@ -381,6 +389,10 @@ function VMDetailsTabNetwork(p: DetailsInnerProps): React.ReactElement {
return <VMNetworksList {...p} />; return <VMNetworksList {...p} />;
} }
function VMDetailsTabCloudInit(p: DetailsInnerProps): React.ReactElement {
return <CloudInitEditor {...p} />;
}
function VMDetailsTabAdvanced(p: DetailsInnerProps): React.ReactElement { function VMDetailsTabAdvanced(p: DetailsInnerProps): React.ReactElement {
return ( return (
<Grid container spacing={2}> <Grid container spacing={2}>