Can customize from UI Cloud init metadata
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
102
virtweb_frontend/src/widgets/forms/CloudInitEditor.tsx
Normal file
102
virtweb_frontend/src/widgets/forms/CloudInitEditor.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -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"}
|
||||||
|
@ -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}>
|
||||||
|
Reference in New Issue
Block a user