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