Can create NAT networks
This commit is contained in:
		@@ -30,6 +30,12 @@ export interface VMDisk {
 | 
			
		||||
  deleteType?: "keepfile" | "deletefile";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type VMNetInterface = VMNetUserspaceSLIRPStack;
 | 
			
		||||
 | 
			
		||||
export interface VMNetUserspaceSLIRPStack {
 | 
			
		||||
  type: "UserspaceSLIRPStack";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface VMInfoInterface {
 | 
			
		||||
  name: string;
 | 
			
		||||
  uuid?: string;
 | 
			
		||||
@@ -43,6 +49,7 @@ interface VMInfoInterface {
 | 
			
		||||
  vnc_access: boolean;
 | 
			
		||||
  iso_file?: string;
 | 
			
		||||
  disks: VMDisk[];
 | 
			
		||||
  networks: VMNetInterface[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class VMInfo implements VMInfoInterface {
 | 
			
		||||
@@ -58,6 +65,7 @@ export class VMInfo implements VMInfoInterface {
 | 
			
		||||
  vnc_access: boolean;
 | 
			
		||||
  iso_file?: string;
 | 
			
		||||
  disks: VMDisk[];
 | 
			
		||||
  networks: VMNetUserspaceSLIRPStack[];
 | 
			
		||||
 | 
			
		||||
  constructor(int: VMInfoInterface) {
 | 
			
		||||
    this.name = int.name;
 | 
			
		||||
@@ -72,6 +80,7 @@ export class VMInfo implements VMInfoInterface {
 | 
			
		||||
    this.vnc_access = int.vnc_access;
 | 
			
		||||
    this.iso_file = int.iso_file;
 | 
			
		||||
    this.disks = int.disks;
 | 
			
		||||
    this.networks = int.networks;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static NewEmpty(): VMInfo {
 | 
			
		||||
@@ -83,6 +92,7 @@ export class VMInfo implements VMInfoInterface {
 | 
			
		||||
      number_vcpu: 1,
 | 
			
		||||
      vnc_access: true,
 | 
			
		||||
      disks: [],
 | 
			
		||||
      networks: [],
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,16 @@
 | 
			
		||||
import { FormControl, InputLabel, MenuItem, Select } from "@mui/material";
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  InputLabel,
 | 
			
		||||
  MenuItem,
 | 
			
		||||
  Select,
 | 
			
		||||
  Typography,
 | 
			
		||||
} from "@mui/material";
 | 
			
		||||
import { TextInput } from "./TextInput";
 | 
			
		||||
 | 
			
		||||
export interface SelectOption {
 | 
			
		||||
  value?: string;
 | 
			
		||||
  label: string;
 | 
			
		||||
  description?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function SelectInput(p: {
 | 
			
		||||
@@ -33,7 +40,18 @@ export function SelectInput(p: {
 | 
			
		||||
            value={e.value}
 | 
			
		||||
            style={{ fontStyle: e.value === undefined ? "italic" : undefined }}
 | 
			
		||||
          >
 | 
			
		||||
            {e.label}
 | 
			
		||||
            <div>
 | 
			
		||||
              {e.label}
 | 
			
		||||
              {e.description && (
 | 
			
		||||
                <Typography
 | 
			
		||||
                  component={"div"}
 | 
			
		||||
                  variant="caption"
 | 
			
		||||
                  style={{ whiteSpace: "normal" }}
 | 
			
		||||
                >
 | 
			
		||||
                  {e.description}
 | 
			
		||||
                </Typography>
 | 
			
		||||
              )}
 | 
			
		||||
            </div>
 | 
			
		||||
          </MenuItem>
 | 
			
		||||
        ))}
 | 
			
		||||
      </Select>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										116
									
								
								virtweb_frontend/src/widgets/forms/VMNetworksList.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								virtweb_frontend/src/widgets/forms/VMNetworksList.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
import { mdiNetworkOutline } from "@mdi/js";
 | 
			
		||||
import Icon from "@mdi/react";
 | 
			
		||||
import DeleteIcon from "@mui/icons-material/Delete";
 | 
			
		||||
import {
 | 
			
		||||
  Avatar,
 | 
			
		||||
  Button,
 | 
			
		||||
  IconButton,
 | 
			
		||||
  ListItem,
 | 
			
		||||
  ListItemAvatar,
 | 
			
		||||
  ListItemText,
 | 
			
		||||
  Tooltip,
 | 
			
		||||
} from "@mui/material";
 | 
			
		||||
import { VMInfo, VMNetInterface } from "../../api/VMApi";
 | 
			
		||||
import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
 | 
			
		||||
import { SelectInput } from "./SelectInput";
 | 
			
		||||
 | 
			
		||||
export function VMNetworksList(p: {
 | 
			
		||||
  vm: VMInfo;
 | 
			
		||||
  onChange?: () => void;
 | 
			
		||||
  editable: boolean;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const addNew = () => {
 | 
			
		||||
    p.vm.networks.push({ type: "UserspaceSLIRPStack" });
 | 
			
		||||
    p.onChange?.();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      {/* networks list */}
 | 
			
		||||
      {p.vm.networks.map((n, num) => (
 | 
			
		||||
        <NetworkInfo
 | 
			
		||||
          key={num}
 | 
			
		||||
          editable={p.editable}
 | 
			
		||||
          network={n}
 | 
			
		||||
          onChange={p.onChange}
 | 
			
		||||
          removeFromList={() => {
 | 
			
		||||
            p.vm.networks.splice(num, 1);
 | 
			
		||||
            p.onChange?.();
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      ))}
 | 
			
		||||
 | 
			
		||||
      {p.editable && (
 | 
			
		||||
        <Button onClick={addNew}>Add a new network interface</Button>
 | 
			
		||||
      )}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function NetworkInfo(p: {
 | 
			
		||||
  editable: boolean;
 | 
			
		||||
  network: VMNetInterface;
 | 
			
		||||
  onChange?: () => void;
 | 
			
		||||
  removeFromList: () => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const confirm = useConfirm();
 | 
			
		||||
  const deleteNetwork = async () => {
 | 
			
		||||
    if (
 | 
			
		||||
      !(await confirm("Do you really want to remove this network interface?"))
 | 
			
		||||
    )
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    p.removeFromList();
 | 
			
		||||
    p.onChange?.();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <ListItem
 | 
			
		||||
        secondaryAction={
 | 
			
		||||
          p.editable && (
 | 
			
		||||
            <IconButton
 | 
			
		||||
              edge="end"
 | 
			
		||||
              aria-label="remove network"
 | 
			
		||||
              onClick={deleteNetwork}
 | 
			
		||||
            >
 | 
			
		||||
              <Tooltip title="Remove network">
 | 
			
		||||
                <DeleteIcon />
 | 
			
		||||
              </Tooltip>
 | 
			
		||||
            </IconButton>
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <ListItemAvatar>
 | 
			
		||||
          <Avatar>
 | 
			
		||||
            <Icon path={mdiNetworkOutline} />
 | 
			
		||||
          </Avatar>
 | 
			
		||||
        </ListItemAvatar>
 | 
			
		||||
        <ListItemText
 | 
			
		||||
          primary={
 | 
			
		||||
            p.editable ? (
 | 
			
		||||
              <SelectInput
 | 
			
		||||
                label=""
 | 
			
		||||
                editable
 | 
			
		||||
                value={p.network.type}
 | 
			
		||||
                onValueChange={(v) => {
 | 
			
		||||
                  p.network.type = v as any;
 | 
			
		||||
                }}
 | 
			
		||||
                options={[
 | 
			
		||||
                  {
 | 
			
		||||
                    label: "Userspace SLIRP stack",
 | 
			
		||||
                    value: "UserspaceSLIRPStack",
 | 
			
		||||
                    description:
 | 
			
		||||
                      "Provides a virtual LAN with NAT to the outside world. The virtual network has DHCP & DNS services",
 | 
			
		||||
                  },
 | 
			
		||||
                ]}
 | 
			
		||||
              />
 | 
			
		||||
            ) : (
 | 
			
		||||
              p.network.type
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        />
 | 
			
		||||
      </ListItem>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@@ -13,6 +13,7 @@ import { VMDisksList } from "../forms/VMDisksList";
 | 
			
		||||
import { VMSelectIsoInput } from "../forms/VMSelectIsoInput";
 | 
			
		||||
import { VMScreenshot } from "./VMScreenshot";
 | 
			
		||||
import { ResAutostartInput } from "../forms/ResAutostartInput";
 | 
			
		||||
import { VMNetworksList } from "../forms/VMNetworksList";
 | 
			
		||||
 | 
			
		||||
interface DetailsProps {
 | 
			
		||||
  vm: VMInfo;
 | 
			
		||||
@@ -202,6 +203,11 @@ function VMDetailsInner(
 | 
			
		||||
        />
 | 
			
		||||
        <VMDisksList vm={p.vm} editable={p.editable} onChange={p.onChange} />
 | 
			
		||||
      </EditSection>
 | 
			
		||||
 | 
			
		||||
      {/* Networks section */}
 | 
			
		||||
      <EditSection title="Networks">
 | 
			
		||||
        <VMNetworksList vm={p.vm} editable={p.editable} onChange={p.onChange} />
 | 
			
		||||
      </EditSection>
 | 
			
		||||
    </Grid>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user