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;
 | 
			
		||||
  helperText?: string;
 | 
			
		||||
  disabled?: boolean;
 | 
			
		||||
  endAdornment?: React.ReactNode;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  if (!p.editable && (p.value ?? "") === "") return <></>;
 | 
			
		||||
 | 
			
		||||
@@ -51,6 +52,7 @@ export function TextInput(p: {
 | 
			
		||||
        input: {
 | 
			
		||||
          readOnly: !p.editable,
 | 
			
		||||
          type: p.type,
 | 
			
		||||
          endAdornment: p.endAdornment,
 | 
			
		||||
        },
 | 
			
		||||
      }}
 | 
			
		||||
      variant={"standard"}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import Grid from "@mui/material/Grid";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { useNavigate } from "react-router-dom";
 | 
			
		||||
import { validate as validateUUID } from "uuid";
 | 
			
		||||
import { DiskImage, DiskImageApi } from "../../api/DiskImageApi";
 | 
			
		||||
import { GroupApi } from "../../api/GroupApi";
 | 
			
		||||
import { IsoFile, IsoFilesApi } from "../../api/IsoFilesApi";
 | 
			
		||||
import { NWFilter, NWFilterApi } from "../../api/NWFilterApi";
 | 
			
		||||
@@ -18,6 +19,7 @@ import { AsyncWidget } from "../AsyncWidget";
 | 
			
		||||
import { TabsWidget } from "../TabsWidget";
 | 
			
		||||
import { XMLAsyncWidget } from "../XMLWidget";
 | 
			
		||||
import { CheckboxInput } from "../forms/CheckboxInput";
 | 
			
		||||
import { CloudInitEditor } from "../forms/CloudInitEditor";
 | 
			
		||||
import { EditSection } from "../forms/EditSection";
 | 
			
		||||
import { OEMStringFormWidget } from "../forms/OEMStringFormWidget";
 | 
			
		||||
import { ResAutostartInput } from "../forms/ResAutostartInput";
 | 
			
		||||
@@ -27,7 +29,6 @@ import { VMDisksList } from "../forms/VMDisksList";
 | 
			
		||||
import { VMNetworksList } from "../forms/VMNetworksList";
 | 
			
		||||
import { VMSelectIsoInput } from "../forms/VMSelectIsoInput";
 | 
			
		||||
import { VMScreenshot } from "./VMScreenshot";
 | 
			
		||||
import { DiskImage, DiskImageApi } from "../../api/DiskImageApi";
 | 
			
		||||
 | 
			
		||||
interface DetailsProps {
 | 
			
		||||
  vm: VMInfo;
 | 
			
		||||
@@ -89,6 +90,7 @@ enum VMTab {
 | 
			
		||||
  General = 0,
 | 
			
		||||
  Storage,
 | 
			
		||||
  Network,
 | 
			
		||||
  CloudInit,
 | 
			
		||||
  Advanced,
 | 
			
		||||
  XML,
 | 
			
		||||
  Danger,
 | 
			
		||||
@@ -116,6 +118,11 @@ function VMDetailsInner(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
          { label: "General", value: VMTab.General, visible: true },
 | 
			
		||||
          { label: "Storage", value: VMTab.Storage, 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 },
 | 
			
		||||
 | 
			
		||||
          {
 | 
			
		||||
@@ -135,6 +142,7 @@ function VMDetailsInner(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
      {currTab === VMTab.General && <VMDetailsTabGeneral {...p} />}
 | 
			
		||||
      {currTab === VMTab.Storage && <VMDetailsTabStorage {...p} />}
 | 
			
		||||
      {currTab === VMTab.Network && <VMDetailsTabNetwork {...p} />}
 | 
			
		||||
      {currTab === VMTab.CloudInit && <VMDetailsTabCloudInit {...p} />}
 | 
			
		||||
      {currTab === VMTab.Advanced && <VMDetailsTabAdvanced {...p} />}
 | 
			
		||||
      {currTab === VMTab.XML && <VMDetailsTabXML {...p} />}
 | 
			
		||||
      {currTab === VMTab.Danger && <VMDetailsTabDanger {...p} />}
 | 
			
		||||
@@ -381,6 +389,10 @@ function VMDetailsTabNetwork(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
  return <VMNetworksList {...p} />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function VMDetailsTabCloudInit(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
  return <CloudInitEditor {...p} />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function VMDetailsTabAdvanced(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
  return (
 | 
			
		||||
    <Grid container spacing={2}>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user