fixes and improvements

This commit is contained in:
2026-05-11 16:27:07 +02:00
parent 3731326c08
commit 67a619e148
2 changed files with 171 additions and 48 deletions
@@ -44,7 +44,32 @@ def get_monitoring_overview(
) -> dict[str, Any]:
"""Return one lightweight monitoring row per configured machine."""
machines = store.list_machines()
rows = [collect_machine_overview(machine, store) for machine in machines]
rows: list[dict[str, Any]] = []
for machine in machines:
try:
rows.append(collect_machine_overview(machine, store))
except Exception as exc:
logger.exception(
"Dashboard monitoring overview failed for machine_id=%s",
machine.get("id"),
)
rows.append({
"machine": {k: machine.get(k) for k in ("id", "name", "mode", "enabled", "host", "port", "username", "media_root", "path_prefix", "notes")},
"status": "",
"status_error": str(exc),
"metrics_error": str(exc),
"disk_error": str(exc),
"sample_count": 0,
"latest_sample": None,
"cpu_summary": None,
"iowait_summary": None,
"mem_summary": None,
"net_rx_summary": None,
"net_tx_summary": None,
"disk_read_summary": None,
"disk_write_summary": None,
"disk": None,
})
poller = get_monitoring_poller().snapshot()
enabled_count = sum(1 for machine in machines if machine.get("enabled"))
logger.info("Dashboard monitoring machines=%s enabled=%s", len(machines), enabled_count)
@@ -11,6 +11,7 @@ import {
TableHead,
TableRow,
TableSortLabel,
Tooltip,
Typography,
} from "@mui/material";
import type {
@@ -102,6 +103,19 @@ function sortableText(value: string) {
return value.toLowerCase();
}
function getStatusChipProps(row: MonitoringMachineOverview) {
if (row.status_error) {
return { color: "error" as const, label: row.status_error };
}
if (row.status === "running") {
return { color: "success" as const, label: "running" };
}
if (row.status === "not running") {
return { color: "warning" as const, label: "not running" };
}
return { color: "default" as const, label: row.status || "-" };
}
function metricSortValue(
row: MonitoringMachineOverview,
key: SortKey,
@@ -136,6 +150,34 @@ function metricSortValue(
}
}
function NoDataCell() {
return (
<Stack
sx={{
width: "100%",
minWidth: 0,
height: "100%",
minHeight: 118,
textAlign: "center",
justifyContent: "center",
py: 0.5,
}}
>
<Typography
variant="caption"
color="text.secondary"
sx={{
fontSize: "0.7rem",
fontStyle: "italic",
lineHeight: 1.2,
}}
>
No data
</Typography>
</Stack>
);
}
function MetricCell({
value,
min,
@@ -441,7 +483,7 @@ export function MonitoringOverviewTable({
) : (
sortedRows.map((row) => {
const machine = row.machine;
const status = row.status || row.status_error || "-";
const hasMetrics = row.sample_count > 0;
const cpu = formatSummary(
row.cpu_summary,
(value) => `${value.toFixed(1)}%`,
@@ -524,62 +566,106 @@ export function MonitoringOverviewTable({
</Stack>
</TableCell>
<TableCell sx={{ width: 90 }}>{machine.mode}</TableCell>
<TableCell sx={{ width: 120 }}>
<TableCell sx={{ width: 120 }}>
{(() => {
const chip = getStatusChipProps(row);
return (
<Tooltip
title={
row.metrics_error
? `Metrics: ${row.metrics_error}`
: row.sample_count === 0
? "No metrics collected yet. Start the collector to begin monitoring."
: ""
}
arrow
>
<Chip
size="small"
variant="outlined"
color={row.status_error ? "error" : "success"}
label={status}
color={chip.color}
label={chip.label}
/>
</Tooltip>
);
})()}
</TableCell>
<TableCell align="center">
{hasMetrics ? (
<MetricCell
value={cpu.value}
min={cpu.min}
max={cpu.max}
/>
) : (
<NoDataCell />
)}
</TableCell>
<TableCell align="center">
<MetricCell
value={cpu.value}
min={cpu.min}
max={cpu.max}
/>
{hasMetrics ? (
<MetricCell
value={iowait.value}
min={iowait.min}
max={iowait.max}
/>
) : (
<NoDataCell />
)}
</TableCell>
<TableCell align="center">
<MetricCell
value={iowait.value}
min={iowait.min}
max={iowait.max}
/>
{hasMetrics ? (
<MetricCell
value={mem.value}
min={mem.min}
max={mem.max}
/>
) : (
<NoDataCell />
)}
</TableCell>
<TableCell align="center">
<MetricCell
value={mem.value}
min={mem.min}
max={mem.max}
/>
{hasMetrics ? (
<MetricCell
value={netDown.value}
min={netDown.min}
max={netDown.max}
/>
) : (
<NoDataCell />
)}
</TableCell>
<TableCell align="center">
<MetricCell
value={netDown.value}
min={netDown.min}
max={netDown.max}
/>
{hasMetrics ? (
<MetricCell
value={netUp.value}
min={netUp.min}
max={netUp.max}
/>
) : (
<NoDataCell />
)}
</TableCell>
<TableCell align="center">
<MetricCell
value={netUp.value}
min={netUp.min}
max={netUp.max}
/>
{hasMetrics ? (
<MetricCell
value={diskRead.value}
min={diskRead.min}
max={diskRead.max}
/>
) : (
<NoDataCell />
)}
</TableCell>
<TableCell align="center">
<MetricCell
value={diskRead.value}
min={diskRead.min}
max={diskRead.max}
/>
</TableCell>
<TableCell align="center">
<MetricCell
value={diskWrite.value}
min={diskWrite.min}
max={diskWrite.max}
/>
{hasMetrics ? (
<MetricCell
value={diskWrite.value}
min={diskWrite.min}
max={diskWrite.max}
/>
) : (
<NoDataCell />
)}
</TableCell>
<TableCell align="center">
{disk ? (
@@ -588,22 +674,34 @@ export function MonitoringOverviewTable({
min={disk.min}
max={disk.max}
/>
) : (
) : hasMetrics ? (
"-"
) : (
<NoDataCell />
)}
</TableCell>
<TableCell sx={{ width: 180 }}>
<Stack spacing={0.1} sx={{ alignItems: "center" }}>
{updated.map((line) => (
{hasMetrics ? (
updated.map((line) => (
<Typography
key={line}
variant="caption"
color="text.secondary"
sx={{ lineHeight: 1.1 }}
>
{line}
</Typography>
))
) : (
<Typography
key={line}
variant="caption"
color="text.secondary"
sx={{ lineHeight: 1.1 }}
sx={{ lineHeight: 1.1, fontStyle: "italic" }}
>
{line}
No data yet
</Typography>
))}
)}
</Stack>
</TableCell>
</TableRow>