Can change VM groups
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Pierre HUBERT 2024-11-02 18:02:03 +01:00
parent c908d00c62
commit 20e6d7931e
6 changed files with 76 additions and 2 deletions

View File

@ -40,6 +40,7 @@ struct ServerConstraints {
vnc_token_duration: u64, vnc_token_duration: u64,
vm_name_size: LenConstraints, vm_name_size: LenConstraints,
vm_title_size: LenConstraints, vm_title_size: LenConstraints,
group_id_size: LenConstraints,
memory_size: LenConstraints, memory_size: LenConstraints,
disk_name_size: LenConstraints, disk_name_size: LenConstraints,
disk_size: LenConstraints, disk_size: LenConstraints,
@ -72,6 +73,7 @@ pub async fn static_config(local_auth: LocalAuthEnabled) -> impl Responder {
vm_name_size: LenConstraints { min: 2, max: 50 }, vm_name_size: LenConstraints { min: 2, max: 50 },
vm_title_size: LenConstraints { min: 0, max: 50 }, vm_title_size: LenConstraints { min: 0, max: 50 },
group_id_size: LenConstraints { min: 3, max: 50 },
memory_size: LenConstraints { memory_size: LenConstraints {
min: constants::MIN_VM_MEMORY, min: constants::MIN_VM_MEMORY,
max: constants::MAX_VM_MEMORY, max: constants::MAX_VM_MEMORY,

View File

@ -211,7 +211,7 @@ async fn main() -> std::io::Result<()> {
) )
.route("/api/vnc", web::get().to(vm_controller::vnc)) .route("/api/vnc", web::get().to(vm_controller::vnc))
// Groups controller // Groups controller
.route("/api/groups/list", web::get().to(groups_controller::list)) .route("/api/group/list", web::get().to(groups_controller::list))
// Network controller // Network controller
.route( .route(
"/api/network/create", "/api/network/create",

View File

@ -0,0 +1,15 @@
import { APIClient } from "./ApiClient";
export class GroupApi {
/**
* Get the entire list of networks
*/
static async GetList(): Promise<string[]> {
return (
await APIClient.exec({
method: "GET",
uri: "/group/list",
})
).data;
}
}

View File

@ -16,6 +16,7 @@ export interface ServerConstraints {
vnc_token_duration: number; vnc_token_duration: number;
vm_name_size: LenConstraint; vm_name_size: LenConstraint;
vm_title_size: LenConstraint; vm_title_size: LenConstraint;
group_id_size: LenConstraint;
memory_size: LenConstraint; memory_size: LenConstraint;
disk_name_size: LenConstraint; disk_name_size: LenConstraint;
disk_size: LenConstraint; disk_size: LenConstraint;

View File

@ -63,6 +63,7 @@ interface VMInfoInterface {
genid?: string; genid?: string;
title?: string; title?: string;
description?: string; description?: string;
group?: string;
boot_type: "UEFI" | "UEFISecureBoot"; boot_type: "UEFI" | "UEFISecureBoot";
architecture: "i686" | "x86_64"; architecture: "i686" | "x86_64";
memory: number; memory: number;
@ -80,6 +81,7 @@ export class VMInfo implements VMInfoInterface {
genid?: string; genid?: string;
title?: string; title?: string;
description?: string; description?: string;
group?: string;
boot_type: "UEFI" | "UEFISecureBoot"; boot_type: "UEFI" | "UEFISecureBoot";
architecture: "i686" | "x86_64"; architecture: "i686" | "x86_64";
number_vcpu: number; number_vcpu: number;
@ -96,6 +98,7 @@ export class VMInfo implements VMInfoInterface {
this.genid = int.genid; this.genid = int.genid;
this.title = int.title; this.title = int.title;
this.description = int.description; this.description = int.description;
this.group = int.group;
this.boot_type = int.boot_type; this.boot_type = int.boot_type;
this.architecture = int.architecture; this.architecture = int.architecture;
this.number_vcpu = int.number_vcpu; this.number_vcpu = int.number_vcpu;

View File

@ -1,8 +1,11 @@
import { Button } from "@mui/material"; import AddIcon from "@mui/icons-material/Add";
import ListIcon from "@mui/icons-material/List";
import { Button, IconButton, Tooltip } from "@mui/material";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid2";
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 { 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";
import { NetworkApi, NetworkInfo } from "../../api/NetworksApi"; import { NetworkApi, NetworkInfo } from "../../api/NetworksApi";
@ -32,6 +35,7 @@ interface DetailsProps {
} }
export function VMDetails(p: DetailsProps): React.ReactElement { export function VMDetails(p: DetailsProps): React.ReactElement {
const [groupsList, setGroupsList] = React.useState<string[] | any>();
const [isoList, setIsoList] = React.useState<IsoFile[] | any>(); const [isoList, setIsoList] = React.useState<IsoFile[] | any>();
const [vcpuCombinations, setVCPUCombinations] = React.useState< const [vcpuCombinations, setVCPUCombinations] = React.useState<
number[] | any number[] | any
@ -42,6 +46,7 @@ export function VMDetails(p: DetailsProps): React.ReactElement {
>(); >();
const load = async () => { const load = async () => {
setGroupsList(await GroupApi.GetList());
setIsoList(await IsoFilesApi.GetList()); setIsoList(await IsoFilesApi.GetList());
setVCPUCombinations(await ServerApi.NumberVCPUs()); setVCPUCombinations(await ServerApi.NumberVCPUs());
setNetworksList(await NetworkApi.GetList()); setNetworksList(await NetworkApi.GetList());
@ -55,6 +60,7 @@ export function VMDetails(p: DetailsProps): React.ReactElement {
errMsg="Failed to load the list of ISO files" errMsg="Failed to load the list of ISO files"
build={() => ( build={() => (
<VMDetailsInner <VMDetailsInner
groupsList={groupsList}
isoList={isoList} isoList={isoList}
vcpuCombinations={vcpuCombinations} vcpuCombinations={vcpuCombinations}
networksList={networksList} networksList={networksList}
@ -75,6 +81,7 @@ enum VMTab {
} }
type DetailsInnerProps = DetailsProps & { type DetailsInnerProps = DetailsProps & {
groupsList: string[];
isoList: IsoFile[]; isoList: IsoFile[];
vcpuCombinations: number[]; vcpuCombinations: number[];
networksList: NetworkInfo[]; networksList: NetworkInfo[];
@ -117,6 +124,8 @@ function VMDetailsInner(p: DetailsInnerProps): React.ReactElement {
} }
function VMDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement { function VMDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement {
const [addGroup, setAddGroup] = React.useState(false);
return ( return (
<Grid container spacing={2}> <Grid container spacing={2}>
{ {
@ -175,6 +184,50 @@ function VMDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement {
}} }}
multiline={true} multiline={true}
/> />
<div style={{ display: "flex" }}>
{addGroup ? (
<TextInput
label="Group"
editable={p.editable}
value={p.vm.group}
onValueChange={(v) => {
p.vm.group = v;
p.onChange?.();
}}
size={ServerApi.Config.constraints.group_id_size}
/>
) : (
<SelectInput
editable={p.editable}
label="Group"
onValueChange={(v) => {
p.vm.group = v! as any;
p.onChange?.();
}}
value={p.vm.group}
options={[
{ label: "None" },
...p.groupsList.map((g) => {
return { value: g, label: g };
}),
]}
/>
)}
{p.editable && (
<Tooltip
title={
addGroup
? "Use an existing group"
: "Add a new group instead of using existing one"
}
>
<IconButton onClick={() => setAddGroup(!addGroup)}>
{addGroup ? <ListIcon /> : <AddIcon />}
</IconButton>
</Tooltip>
)}
</div>
</EditSection> </EditSection>
{/* General section */} {/* General section */}