Guest drawer fallback content for agentless guests (refs #563)
This commit is contained in:
parent
e367cd384e
commit
551a5d23ed
1 changed files with 133 additions and 100 deletions
|
|
@ -52,7 +52,7 @@ export function GuestRow(props: GuestRowProps) {
|
||||||
const [drawerOpen, setDrawerOpen] = createSignal(drawerState.get(initialGuestId) ?? false);
|
const [drawerOpen, setDrawerOpen] = createSignal(drawerState.get(initialGuestId) ?? false);
|
||||||
const guestId = createMemo(() => buildGuestId(props.guest));
|
const guestId = createMemo(() => buildGuestId(props.guest));
|
||||||
|
|
||||||
const hasMultipleDisks = createMemo(() => (props.guest.disks?.length ?? 0) > 1);
|
const hasFilesystemDetails = createMemo(() => (props.guest.disks?.length ?? 0) > 0);
|
||||||
const ipAddresses = createMemo(() => props.guest.ipAddresses ?? []);
|
const ipAddresses = createMemo(() => props.guest.ipAddresses ?? []);
|
||||||
const networkInterfaces = createMemo(() => props.guest.networkInterfaces ?? []);
|
const networkInterfaces = createMemo(() => props.guest.networkInterfaces ?? []);
|
||||||
const hasNetworkInterfaces = createMemo(() => networkInterfaces().length > 0);
|
const hasNetworkInterfaces = createMemo(() => networkInterfaces().length > 0);
|
||||||
|
|
@ -95,16 +95,19 @@ export function GuestRow(props: GuestRowProps) {
|
||||||
}
|
}
|
||||||
return lines.length > 0 ? lines : undefined;
|
return lines.length > 0 ? lines : undefined;
|
||||||
});
|
});
|
||||||
const memoryTooltip = createMemo(() =>
|
const memoryTooltip = createMemo(() => memoryExtraLines()?.join('\n') ?? undefined);
|
||||||
memoryExtraLines()?.join('\n') ?? undefined,
|
const hasDrawerContent = createMemo(
|
||||||
|
() =>
|
||||||
|
hasOsInfo() ||
|
||||||
|
ipAddresses().length > 0 ||
|
||||||
|
(memoryExtraLines()?.length ?? 0) > 0 ||
|
||||||
|
hasFilesystemDetails() ||
|
||||||
|
hasNetworkInterfaces(),
|
||||||
);
|
);
|
||||||
const canShowDrawer = createMemo(() =>
|
const hasFallbackContent = createMemo(
|
||||||
hasOsInfo() ||
|
() => !hasDrawerContent() && (props.guest.type === 'qemu' || props.guest.type === 'lxc'),
|
||||||
ipAddresses().length > 0 ||
|
|
||||||
(memoryExtraLines() && memoryExtraLines()!.length > 0) ||
|
|
||||||
hasMultipleDisks() ||
|
|
||||||
hasNetworkInterfaces(),
|
|
||||||
);
|
);
|
||||||
|
const canShowDrawer = createMemo(() => hasDrawerContent() || hasFallbackContent());
|
||||||
|
|
||||||
createEffect(on(guestId, (id) => {
|
createEffect(on(guestId, (id) => {
|
||||||
const stored = drawerState.get(id);
|
const stored = drawerState.get(id);
|
||||||
|
|
@ -408,101 +411,131 @@ export function GuestRow(props: GuestRowProps) {
|
||||||
drawerDisabled() ? 'opacity-50 saturate-75 pointer-events-none' : ''
|
drawerDisabled() ? 'opacity-50 saturate-75 pointer-events-none' : ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Show when={hasOsInfo() || ipAddresses().length > 0}>
|
<Show
|
||||||
<div class="min-w-[220px] rounded border border-gray-200 bg-white/70 p-2 shadow-sm dark:border-gray-600/70 dark:bg-gray-900/30">
|
when={hasDrawerContent()}
|
||||||
<div class="text-[11px] font-medium text-gray-700 dark:text-gray-200">Guest Overview</div>
|
fallback={
|
||||||
<div class="mt-1 space-y-1">
|
<div class="min-w-[220px] rounded border border-gray-200 bg-white/70 p-2 shadow-sm dark:border-gray-600/70 dark:bg-gray-900/30">
|
||||||
<Show when={hasOsInfo()}>
|
<div class="text-[11px] font-medium text-gray-700 dark:text-gray-200">
|
||||||
<div class="flex flex-wrap items-center gap-1 text-gray-600 dark:text-gray-300">
|
Guest details unavailable
|
||||||
<Show when={osName().length > 0}>
|
</div>
|
||||||
<span class="font-medium" title={osName()}>{osName()}</span>
|
<div class="mt-1 space-y-1 text-gray-600 dark:text-gray-300">
|
||||||
</Show>
|
<Show
|
||||||
<Show when={osName().length > 0 && osVersion().length > 0}>
|
when={isVM(props.guest)}
|
||||||
<span class="text-gray-400 dark:text-gray-500">•</span>
|
fallback={
|
||||||
</Show>
|
<p>
|
||||||
<Show when={osVersion().length > 0}>
|
Start this container and ensure the Pulse user has sufficient Proxmox
|
||||||
<span title={osVersion()}>{osVersion()}</span>
|
permissions to collect guest metrics.
|
||||||
</Show>
|
</p>
|
||||||
</div>
|
}
|
||||||
</Show>
|
>
|
||||||
<Show when={ipAddresses().length > 0}>
|
<p>{getDiskStatusTooltip()}</p>
|
||||||
<div class="flex flex-wrap gap-1">
|
<p>
|
||||||
<For each={ipAddresses()}>
|
Install and run the qemu-guest-agent inside this VM so Pulse can surface
|
||||||
{(ip) => (
|
OS, network, and filesystem details.
|
||||||
<span class="rounded bg-blue-100 px-1.5 py-0.5 text-blue-700 dark:bg-blue-900/40 dark:text-blue-200">
|
</p>
|
||||||
{ip}
|
</Show>
|
||||||
</span>
|
</div>
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
</Show>
|
>
|
||||||
|
<>
|
||||||
<Show when={memoryExtraLines() && memoryExtraLines()!.length > 0}>
|
<Show when={hasOsInfo() || ipAddresses().length > 0}>
|
||||||
<div class="min-w-[220px] rounded border border-gray-200 bg-white/70 p-2 shadow-sm dark:border-gray-600/70 dark:bg-gray-900/30">
|
<div class="min-w-[220px] rounded border border-gray-200 bg-white/70 p-2 shadow-sm dark:border-gray-600/70 dark:bg-gray-900/30">
|
||||||
<div class="text-[11px] font-medium text-gray-700 dark:text-gray-200">Memory</div>
|
<div class="text-[11px] font-medium text-gray-700 dark:text-gray-200">Guest Overview</div>
|
||||||
<div class="mt-1 space-y-1 text-gray-600 dark:text-gray-300">
|
<div class="mt-1 space-y-1">
|
||||||
<For each={memoryExtraLines()!}>{(line) => <div>{line}</div>}</For>
|
<Show when={hasOsInfo()}>
|
||||||
</div>
|
<div class="flex flex-wrap items-center gap-1 text-gray-600 dark:text-gray-300">
|
||||||
</div>
|
<Show when={osName().length > 0}>
|
||||||
</Show>
|
<span class="font-medium" title={osName()}>{osName()}</span>
|
||||||
|
|
||||||
<Show when={hasMultipleDisks() && props.guest.disks && props.guest.disks.length > 0}>
|
|
||||||
<div class="min-w-[220px] rounded border border-gray-200 bg-white/70 p-2 shadow-sm dark:border-gray-600/70 dark:bg-gray-900/30">
|
|
||||||
<div class="text-[11px] font-medium text-gray-700 dark:text-gray-200">Filesystems</div>
|
|
||||||
<div class="mt-1 text-gray-600 dark:text-gray-300">
|
|
||||||
<DiskList
|
|
||||||
disks={props.guest.disks || []}
|
|
||||||
diskStatusReason={isVM(props.guest) ? props.guest.diskStatusReason : undefined}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
|
|
||||||
<Show when={hasNetworkInterfaces()}>
|
|
||||||
<div class="min-w-[220px] flex-1 rounded border border-gray-200 bg-white/70 p-2 shadow-sm dark:border-gray-600/70 dark:bg-gray-900/30">
|
|
||||||
<div class="text-[11px] font-medium text-gray-700 dark:text-gray-200">Network Interfaces</div>
|
|
||||||
<div class="mt-1 text-[10px] text-gray-400 dark:text-gray-500">Row charts show current rate; totals below are cumulative since boot.</div>
|
|
||||||
<div class="mt-1 space-y-1 text-gray-600 dark:text-gray-300">
|
|
||||||
<For each={networkInterfaces()}>
|
|
||||||
{(iface) => {
|
|
||||||
const addresses = iface.addresses ?? [];
|
|
||||||
const hasTraffic = (iface.rxBytes ?? 0) > 0 || (iface.txBytes ?? 0) > 0;
|
|
||||||
return (
|
|
||||||
<div class="space-y-1 rounded border border-dashed border-gray-200 p-2 last:mb-0 dark:border-gray-700">
|
|
||||||
<div class="flex items-center gap-2 font-medium text-gray-700 dark:text-gray-200">
|
|
||||||
<span class="truncate" title={iface.name}>{iface.name || 'interface'}</span>
|
|
||||||
<Show when={iface.mac}>
|
|
||||||
<span class="text-[10px] text-gray-400 dark:text-gray-500" title={iface.mac}>
|
|
||||||
{iface.mac}
|
|
||||||
</span>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<Show when={addresses.length > 0}>
|
|
||||||
<div class="flex flex-wrap gap-1">
|
|
||||||
<For each={addresses}>
|
|
||||||
{(ip) => (
|
|
||||||
<span class="rounded bg-blue-100 px-1.5 py-0.5 text-blue-700 dark:bg-blue-900/40 dark:text-blue-200">
|
|
||||||
{ip}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={hasTraffic}>
|
<Show when={osName().length > 0 && osVersion().length > 0}>
|
||||||
<div class="flex items-center gap-3 text-[10px] text-gray-500 dark:text-gray-400">
|
<span class="text-gray-400 dark:text-gray-500">•</span>
|
||||||
<span>Total RX {formatBytes(iface.rxBytes ?? 0)}</span>
|
</Show>
|
||||||
<span>Total TX {formatBytes(iface.txBytes ?? 0)}</span>
|
<Show when={osVersion().length > 0}>
|
||||||
</div>
|
<span title={osVersion()}>{osVersion()}</span>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
);
|
</Show>
|
||||||
}}
|
<Show when={ipAddresses().length > 0}>
|
||||||
</For>
|
<div class="flex flex-wrap gap-1">
|
||||||
</div>
|
<For each={ipAddresses()}>
|
||||||
</div>
|
{(ip) => (
|
||||||
|
<span class="rounded bg-blue-100 px-1.5 py-0.5 text-blue-700 dark:bg-blue-900/40 dark:text-blue-200">
|
||||||
|
{ip}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={memoryExtraLines() && memoryExtraLines()!.length > 0}>
|
||||||
|
<div class="min-w-[220px] rounded border border-gray-200 bg-white/70 p-2 shadow-sm dark:border-gray-600/70 dark:bg-gray-900/30">
|
||||||
|
<div class="text-[11px] font-medium text-gray-700 dark:text-gray-200">Memory</div>
|
||||||
|
<div class="mt-1 space-y-1 text-gray-600 dark:text-gray-300">
|
||||||
|
<For each={memoryExtraLines()!}>{(line) => <div>{line}</div>}</For>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={hasFilesystemDetails() && props.guest.disks && props.guest.disks.length > 0}>
|
||||||
|
<div class="min-w-[220px] rounded border border-gray-200 bg-white/70 p-2 shadow-sm dark:border-gray-600/70 dark:bg-gray-900/30">
|
||||||
|
<div class="text-[11px] font-medium text-gray-700 dark:text-gray-200">Filesystems</div>
|
||||||
|
<div class="mt-1 text-gray-600 dark:text-gray-300">
|
||||||
|
<DiskList
|
||||||
|
disks={props.guest.disks || []}
|
||||||
|
diskStatusReason={isVM(props.guest) ? props.guest.diskStatusReason : undefined}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={hasNetworkInterfaces()}>
|
||||||
|
<div class="min-w-[220px] flex-1 rounded border border-gray-200 bg-white/70 p-2 shadow-sm dark:border-gray-600/70 dark:bg-gray-900/30">
|
||||||
|
<div class="text-[11px] font-medium text-gray-700 dark:text-gray-200">Network Interfaces</div>
|
||||||
|
<div class="mt-1 text-[10px] text-gray-400 dark:text-gray-500">Row charts show current rate; totals below are cumulative since boot.</div>
|
||||||
|
<div class="mt-1 space-y-1 text-gray-600 dark:text-gray-300">
|
||||||
|
<For each={networkInterfaces()}>
|
||||||
|
{(iface) => {
|
||||||
|
const addresses = iface.addresses ?? [];
|
||||||
|
const hasTraffic = (iface.rxBytes ?? 0) > 0 || (iface.txBytes ?? 0) > 0;
|
||||||
|
return (
|
||||||
|
<div class="space-y-1 rounded border border-dashed border-gray-200 p-2 last:mb-0 dark:border-gray-700">
|
||||||
|
<div class="flex items-center gap-2 font-medium text-gray-700 dark:text-gray-200">
|
||||||
|
<span class="truncate" title={iface.name}>{iface.name || 'interface'}</span>
|
||||||
|
<Show when={iface.mac}>
|
||||||
|
<span class="text-[10px] text-gray-400 dark:text-gray-500" title={iface.mac}>
|
||||||
|
{iface.mac}
|
||||||
|
</span>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
<Show when={addresses.length > 0}>
|
||||||
|
<div class="flex flex-wrap gap-1">
|
||||||
|
<For each={addresses}>
|
||||||
|
{(ip) => (
|
||||||
|
<span class="rounded bg-blue-100 px-1.5 py-0.5 text-blue-700 dark:bg-blue-900/40 dark:text-blue-200">
|
||||||
|
{ip}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<Show when={hasTraffic}>
|
||||||
|
<div class="flex items-center gap-3 text-[10px] text-gray-500 dark:text-gray-400">
|
||||||
|
<span>Total RX {formatBytes(iface.rxBytes ?? 0)}</span>
|
||||||
|
<span>Total TX {formatBytes(iface.txBytes ?? 0)}</span>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue