Add support to bridge option on Web UI
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Pierre HUBERT 2025-05-26 21:02:02 +02:00
parent a8171375a8
commit 6a7af7e6c4
5 changed files with 83 additions and 24 deletions

View File

@ -217,4 +217,16 @@ export class ServerApi {
})
).data;
}
/**
* Get host networks bridges list
*/
static async GetNetworksBridgesList(): Promise<string[]> {
return (
await APIClient.exec({
method: "GET",
uri: "/server/bridges",
})
).data;
}
}

View File

@ -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;

View File

@ -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" && (
<SelectInput
editable={p.editable}
label="Defined network"
options={p.networksList.map((n) => {
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" && (
<SelectInput
editable={p.editable}
label="Host bridge"
options={p.bridgesList.map((n) => {
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" && (
<>
<SelectInput
editable={p.editable}
label="Defined network"
options={p.networksList.map((n) => {
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 */}
<NWFilterSelectInput
editable={p.editable}

View File

@ -725,6 +725,11 @@ export function TokenRightsEditor(p: {
right={{ verb: "GET", path: "/api/server/networks" }}
label="Get list of network cards"
/>
<RouteRight
{...p}
right={{ verb: "GET", path: "/api/server/bridges" }}
label="Get list of network bridges"
/>
</RightsSection>
</>
);

View File

@ -38,6 +38,7 @@ interface DetailsProps {
export function VMDetails(p: DetailsProps): React.ReactElement {
const [groupsList, setGroupsList] = React.useState<string[] | undefined>();
const [isoList, setIsoList] = React.useState<IsoFile[] | undefined>();
const [bridgesList, setBridgesList] = React.useState<string[] | undefined>();
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 {
<VMDetailsInner
groupsList={groupsList!}
isoList={isoList!}
bridgesList={bridgesList!}
vcpuCombinations={vcpuCombinations!}
networksList={networksList!}
networkFiltersList={networkFiltersList!}
@ -87,6 +90,7 @@ enum VMTab {
type DetailsInnerProps = DetailsProps & {
groupsList: string[];
isoList: IsoFile[];
bridgesList: string[];
vcpuCombinations: number[];
networksList: NetworkInfo[];
networkFiltersList: NWFilter[];