From 6a7af7e6c432014d44b0c99c0af7d2bfd00e2871 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Mon, 26 May 2025 21:02:02 +0200 Subject: [PATCH] Add support to bridge option on Web UI --- virtweb_frontend/src/api/ServerApi.ts | 12 +++ virtweb_frontend/src/api/VMApi.ts | 11 ++- .../src/widgets/forms/VMNetworksList.tsx | 75 +++++++++++++------ .../src/widgets/tokens/TokenRightsEditor.tsx | 5 ++ .../src/widgets/vms/VMDetails.tsx | 4 + 5 files changed, 83 insertions(+), 24 deletions(-) diff --git a/virtweb_frontend/src/api/ServerApi.ts b/virtweb_frontend/src/api/ServerApi.ts index 757d5fb..597bfc6 100644 --- a/virtweb_frontend/src/api/ServerApi.ts +++ b/virtweb_frontend/src/api/ServerApi.ts @@ -217,4 +217,16 @@ export class ServerApi { }) ).data; } + + /** + * Get host networks bridges list + */ + static async GetNetworksBridgesList(): Promise { + return ( + await APIClient.exec({ + method: "GET", + uri: "/server/bridges", + }) + ).data; + } } diff --git a/virtweb_frontend/src/api/VMApi.ts b/virtweb_frontend/src/api/VMApi.ts index 415a719..fa88a36 100644 --- a/virtweb_frontend/src/api/VMApi.ts +++ b/virtweb_frontend/src/api/VMApi.ts @@ -50,7 +50,11 @@ export interface VMNetInterfaceFilter { parameters: VMNetInterfaceFilterParams[]; } -export type VMNetInterface = (VMNetUserspaceSLIRPStack | VMNetDefinedNetwork) & +export type VMNetInterface = ( + | VMNetUserspaceSLIRPStack + | VMNetDefinedNetwork + | VMNetBridge +) & VMNetInterfaceBase; export interface VMNetInterfaceBase { @@ -67,6 +71,11 @@ export interface VMNetDefinedNetwork { network: string; } +export interface VMNetBridge { + type: "Bridge"; + bridge: string; +} + interface VMInfoInterface { name: string; uuid?: string; diff --git a/virtweb_frontend/src/widgets/forms/VMNetworksList.tsx b/virtweb_frontend/src/widgets/forms/VMNetworksList.tsx index 5585c5c..6bc46ae 100644 --- a/virtweb_frontend/src/widgets/forms/VMNetworksList.tsx +++ b/virtweb_frontend/src/widgets/forms/VMNetworksList.tsx @@ -29,6 +29,7 @@ export function VMNetworksList(p: { onChange?: () => void; editable: boolean; networksList: NetworkInfo[]; + bridgesList: string[]; networkFiltersList: NWFilter[]; }): React.ReactElement { const addNew = () => { @@ -72,6 +73,7 @@ function NetworkInfoWidget(p: { onChange?: () => void; removeFromList: () => void; networksList: NetworkInfo[]; + bridgesList: string[]; networkFiltersList: NWFilter[]; }): React.ReactElement { const confirm = useConfirm(); @@ -130,6 +132,11 @@ function NetworkInfoWidget(p: { value: "DefinedNetwork", description: "Attach to a defined network", }, + { + label: "Host bridge", + value: "Bridge", + description: "Attach to an host's bridge", + }, ]} /> ) : ( @@ -149,31 +156,53 @@ function NetworkInfoWidget(p: { }} /> + {/* Defined network selection */} {p.network.type === "DefinedNetwork" && ( + { + const chars = [n.forward_mode.toString()]; + if (n.ip_v4) chars.push("IPv4"); + if (n.ip_v6) chars.push("IPv6"); + if (n.description) chars.push(n.description); + + return { + label: n.name, + value: n.name, + description: chars.join(" - "), + }; + })} + value={p.network.network} + onValueChange={(v) => { + if (p.network.type === "DefinedNetwork") + p.network.network = v as any; + p.onChange?.(); + }} + /> + )} + + {/* Bridge selection */} + {p.network.type === "Bridge" && ( + { + return { + label: n, + value: n, + }; + })} + value={p.network.bridge} + onValueChange={(v) => { + if (p.network.type === "Bridge") p.network.bridge = v as any; + p.onChange?.(); + }} + /> + )} + + {p.network.type !== "UserspaceSLIRPStack" && ( <> - { - const chars = [n.forward_mode.toString()]; - if (n.ip_v4) chars.push("IPv4"); - if (n.ip_v6) chars.push("IPv6"); - if (n.description) chars.push(n.description); - - return { - label: n.name, - value: n.name, - description: chars.join(" - "), - }; - })} - value={p.network.network} - onValueChange={(v) => { - if (p.network.type === "DefinedNetwork") - p.network.network = v as any; - p.onChange?.(); - }} - /> - {/* Network Filter */} + ); diff --git a/virtweb_frontend/src/widgets/vms/VMDetails.tsx b/virtweb_frontend/src/widgets/vms/VMDetails.tsx index 7e91f4c..4d7a7b2 100644 --- a/virtweb_frontend/src/widgets/vms/VMDetails.tsx +++ b/virtweb_frontend/src/widgets/vms/VMDetails.tsx @@ -38,6 +38,7 @@ interface DetailsProps { export function VMDetails(p: DetailsProps): React.ReactElement { const [groupsList, setGroupsList] = React.useState(); const [isoList, setIsoList] = React.useState(); + const [bridgesList, setBridgesList] = React.useState(); const [vcpuCombinations, setVCPUCombinations] = React.useState< number[] | undefined >(); @@ -51,6 +52,7 @@ export function VMDetails(p: DetailsProps): React.ReactElement { const load = async () => { setGroupsList(await GroupApi.GetList()); setIsoList(await IsoFilesApi.GetList()); + setBridgesList(await ServerApi.GetNetworksBridgesList()); setVCPUCombinations(await ServerApi.NumberVCPUs()); setNetworksList(await NetworkApi.GetList()); setNetworkFiltersList(await NWFilterApi.GetList()); @@ -65,6 +67,7 @@ export function VMDetails(p: DetailsProps): React.ReactElement {