vm: networks: use isUsed properly, thanks @maxdorx!

docs: add new sponsor
network: obj: fix isUsed typos
This commit is contained in:
hayzamjs
2026-01-16 17:42:16 +00:00
parent b9d46f9efa
commit f4025687ef
11 changed files with 65 additions and 77 deletions
+10 -2
View File
@@ -27,10 +27,18 @@ Were proud to be supported by:
<img src="./docs/sponsors/Alchemilla-Dark.png" alt="Alchemilla" width="150"/>
</picture>
</a>
&emsp;&emsp;&emsp;
<a href="https://iptechnics.com">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./docs/sponsors/IP-Technics-White.png">
<img src="./docs/sponsors/IP-Technics-Dark.png" alt="IPTechnics" width="150"/>
</picture>
</a>
</p>
- [FreeBSD Foundation](https://freebsdfoundation.org)
- [FreeBSD Foundation](https://freebsdfoundation.org)
- [Alchemilla](https://alchemilla.io)
- [IPTechnics](https://iptechnics.com)
You can also support the project by [sponsoring us on GitHub](https://github.com/sponsors/AlchemillaHQ).
@@ -112,7 +120,7 @@ cp -rf ../config.example.json config.json # Edit the config.json file to your li
1. Bhyve doesn't support bootorders yet
Since Bhyve doesn't support bootorders yet, you'll need to configure the order using the UEFI boot menu. You can download
Since Bhyve doesn't support bootorders yet, you'll need to configure the order using the UEFI boot menu. You can download
2. ARM64 support is still pending for Libvirt so the support is not there yet, for everything else it should just work out of the box.
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+8 -2
View File
@@ -212,8 +212,14 @@ func (s *Service) IsObjectUsed(id uint) (bool, error) {
}
}
if jn.IPv4GwID != nil {
if *jn.IPv4GwID == id {
if jn.IPv6GwID != nil {
if *jn.IPv6GwID == id {
return true, nil
}
}
if jn.MacID != nil {
if *jn.MacID == id {
return true, nil
}
}
@@ -4,10 +4,7 @@
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
import type { SwitchList } from '$lib/types/network/switch';
import { Label } from '$lib/components/ui/label/index.js';
import CustomValueInput from '$lib/components/ui/custom-input/value.svelte';
import { onMount } from 'svelte';
import type { NetworkObject } from '$lib/types/network/object';
import type { VM } from '$lib/types/vm/vm';
import { generateMACOptions } from '$lib/utils/network/object';
interface Props {
@@ -15,7 +12,6 @@
mac: string;
emulation: string;
switches: SwitchList;
vms: VM[];
networkObjects: NetworkObject[];
}
@@ -24,19 +20,12 @@
mac = $bindable(),
emulation = $bindable(),
switches,
networkObjects,
vms
networkObjects
}: Props = $props();
let usableMacs = $derived.by(() => {
const usedMacIds = new Set<number>(
vms
.flatMap((vm) => vm.networks.map((net) => net.macId))
.filter((id): id is number => id !== undefined)
);
return networkObjects.filter(
(obj) => obj.type === 'Mac' && obj.entries?.length === 1 && !usedMacIds.has(obj.id)
(obj) => obj.type === 'Mac' && obj.entries?.length === 1 && obj.isUsed === false
);
});
@@ -3,7 +3,6 @@
import SimpleSelect from '$lib/components/custom/SimpleSelect.svelte';
import { Button } from '$lib/components/ui/button/index.js';
import CustomComboBox from '$lib/components/ui/custom-input/combobox.svelte';
import CustomValueInput from '$lib/components/ui/custom-input/value.svelte';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import type { NetworkObject } from '$lib/types/network/object';
import type { SwitchList } from '$lib/types/network/switch';
@@ -15,12 +14,11 @@
interface Props {
open: boolean;
switches: SwitchList;
vms: VM[];
vm: VM | null;
networkObjects: NetworkObject[];
}
let { open = $bindable(), switches, vm, networkObjects, vms }: Props = $props();
let { open = $bindable(), switches, vm, networkObjects }: Props = $props();
let usable = $derived.by(() => {
return [
...(switches.standard ?? []).map((s) => ({
@@ -35,14 +33,8 @@
});
let usableMacs = $derived.by(() => {
const usedMacIds = new Set<number>(
vms
.flatMap((vm) => vm.networks.map((net) => net.macId))
.filter((id): id is number => id !== undefined)
);
return networkObjects.filter(
(obj) => obj.type === 'Mac' && obj.entries?.length === 1 && !usedMacIds.has(obj.id)
(obj) => obj.type === 'Mac' && obj.entries?.length === 1 && obj.isUsed === false
);
});
+1 -1
View File
@@ -4301,7 +4301,7 @@ msgstr "No available/unused switches to attach to"
msgid "VM must be shut off to attach storage"
msgstr "VM must be shut off to attach storage"
#. placeholder {0}: vm?.name
#. placeholder {0}: vm.current.name
#. placeholder {1}: properties.detach.name
#: src/routes/[node]/vm/[node]/network/+page.svelte
msgid "This will detach the VM <b>{0}</b> from the switch <b>{1}</b>"
+1 -1
View File
@@ -4279,7 +4279,7 @@ msgstr "जोड़ने के लिए कोई उपलब्ध/अप
msgid "VM must be shut off to attach storage"
msgstr "संग्रहण जोड़ने के लिए VM को बंद करना आवश्यक है"
#. placeholder {0}: vm?.name
#. placeholder {0}: vm.current.name
#. placeholder {1}: properties.detach.name
#: src/routes/[node]/vm/[node]/network/+page.svelte
msgid "This will detach the VM <b>{0}</b> from the switch <b>{1}</b>"
+1 -1
View File
@@ -4289,7 +4289,7 @@ msgstr ""
msgid "VM must be shut off to attach storage"
msgstr ""
#. placeholder {0}: vm?.name
#. placeholder {0}: vm.current.name
#. placeholder {1}: properties.detach.name
#: src/routes/[node]/vm/[node]/network/+page.svelte
msgid "This will detach the VM <b>{0}</b> from the switch <b>{1}</b>"
@@ -3,7 +3,7 @@
import { getNetworkObjects } from '$lib/api/network/object';
import { getSwitches } from '$lib/api/network/switch';
import { detachNetwork } from '$lib/api/vm/network';
import { getVMDomain, getVMs } from '$lib/api/vm/vm';
import { getVmById, getVMDomain } from '$lib/api/vm/vm';
import AlertDialog from '$lib/components/custom/Dialog/Alert.svelte';
import TreeTable from '$lib/components/custom/TreeTable.svelte';
import Network from '$lib/components/custom/VM/Hardware/Network.svelte';
@@ -16,12 +16,11 @@
import { handleAPIError, updateCache } from '$lib/utils/http';
import { toast } from 'svelte-sonner';
import type { CellComponent } from 'tabulator-tables';
import { resource, useInterval } from 'runed';
import { resource, useInterval, watch } from 'runed';
import { untrack } from 'svelte';
import { storage } from '$lib';
interface Data {
vms: VM[];
vm: VM;
domain: VMDomain;
interfaces: Iface[];
@@ -58,16 +57,16 @@
}
);
const vms = resource(
() => 'vms',
const vm = resource(
() => `vm-${data.rid}`,
async (key) => {
const result = await getVMs();
const result = await getVmById(Number(data.vm.rid), 'rid');
updateCache(key, result);
return result;
},
{
lazy: true,
initialValue: data.vms
initialValue: data.vm
}
);
@@ -102,26 +101,23 @@
if (storage.visible) {
interfaces.refetch();
switches.refetch();
vms.refetch();
vm.refetch();
domain.refetch();
networkObjects.refetch();
}
}
});
$effect(() => {
if (storage.visible) {
untrack(() => {
interfaces.refetch();
switches.refetch();
vms.refetch();
domain.refetch();
networkObjects.refetch();
});
watch(
() => storage.visible,
() => {
interfaces.refetch();
switches.refetch();
vm.refetch();
domain.refetch();
networkObjects.refetch();
}
});
let vm = $derived(vms.current.find((vm) => vm.rid === Number(data.rid)));
);
function generateTableData() {
const rows: Row[] = [];
@@ -145,8 +141,8 @@
}
];
if (vm?.networks) {
for (const network of vm.networks) {
if (vm.current) {
for (const network of vm.current.networks) {
let sw: StandardSwitch | ManualSwitch | null = null;
if (network.switchType === 'standard') {
sw = switches.current.standard?.find((s) => s.id === network.switchId) ?? null;
@@ -236,7 +232,7 @@
<div class="flex h-10 w-full items-center gap-2 border p-2">
<Button
onclick={() => {
if (vm) {
if (vm.current) {
if (usable?.length === 0) {
toast.error('No available/unused switches to attach to', {
position: 'bottom-center'
@@ -278,10 +274,10 @@
<AlertDialog
open={properties.detach.open}
customTitle={`This will detach the VM <b>${vm?.name}</b> from the switch <b>${properties.detach.name}</b>`}
customTitle={`This will detach the VM <b>${vm.current.name}</b> from the switch <b>${properties.detach.name}</b>`}
actions={{
onConfirm: async () => {
let response = await detachNetwork(vm?.rid as number, properties.detach.id as number);
let response = await detachNetwork(vm.current.rid as number, properties.detach.id as number);
if (response.status === 'error') {
handleAPIError(response);
toast.error('Failed to detach network', {
@@ -306,7 +302,6 @@
<Network
bind:open={properties.attach.open}
switches={switches.current}
vms={vms.current}
networkObjects={networkObjects.current}
vm={vm ?? null}
vm={vm.current ?? null}
/>
@@ -1,30 +1,28 @@
import { getInterfaces } from '$lib/api/network/iface';
import { getNetworkObjects } from '$lib/api/network/object.js';
import { getSwitches } from '$lib/api/network/switch';
import { getVMDomain, getVMs } from '$lib/api/vm/vm';
import { getVmById, getVMDomain, getVMs } from '$lib/api/vm/vm';
import { cachedFetch } from '$lib/utils/http';
export async function load({ params }) {
const cacheDuration = 1000 * 60000;
const rid = params.node;
const cacheDuration = 1000 * 60000;
const rid = params.node;
const [vms, domain, interfaces, switches, networkObjects] = await Promise.all([
cachedFetch('vm-list', async () => getVMs(), cacheDuration),
cachedFetch(`vm-domain-${rid}`, async () => getVMDomain(Number(rid)), cacheDuration),
cachedFetch('networkInterfaces', async () => await getInterfaces(), cacheDuration),
cachedFetch('networkSwitches', async () => await getSwitches(), cacheDuration),
cachedFetch('networkObjects', async () => await getNetworkObjects(), cacheDuration)
]);
const [vm, domain, interfaces, switches, networkObjects] = await Promise.all([
cachedFetch(`vm-${rid}`, async () => getVmById(Number(rid), "rid"), cacheDuration),
cachedFetch(`vm-domain-${rid}`, async () => getVMDomain(Number(rid)), cacheDuration),
cachedFetch('networkInterfaces', async () => await getInterfaces(), cacheDuration),
cachedFetch('networkSwitches', async () => await getSwitches(), cacheDuration),
cachedFetch('networkObjects', async () => await getNetworkObjects(), cacheDuration)
]);
const vm = vms.find((vm) => vm.rid === Number(rid));
return {
rid,
domain,
interfaces,
switches,
networkObjects,
vms,
vm
};
return {
rid,
domain,
interfaces,
switches,
networkObjects,
vm
};
}