## Summary Add support for monitoring Proxmox LXC containers through the Capture agent's /api/v1/metrics/proxmox endpoint. This feature is opt-in via a new setting in the infrastructure monitor configuration. ## Backend Changes - Add enableProxmoxContainers field to Monitor schema and types - Update networkService to conditionally fetch Proxmox metrics - Handle HTTP 207 multi-status responses and merge errors - Add containers field to Check schema for storing container data - Update validation schemas for the new setting ## Frontend Changes - Add ContainerStats component with charts and stat boxes - Add Containers tab to infrastructure monitor details (conditional) - Add enableProxmoxContainers switch in monitor create/edit form - Fix chart colors for disk I/O and network I/O to be distinct - Add dotColor2 prop to InfrastructureTooltip for multi-series charts - Add translation keys to all 16 locale files ## How It Works 1. User enables "Proxmox container monitoring" in monitor settings 2. Checkmate appends /proxmox to the metrics URL when fetching 3. Container data is displayed in a dedicated tab with: - CPU, memory, disk, swap, and uptime stat boxes - Interactive charts for CPU, memory, disk I/O, network I/O 4. Errors from Capture are merged and logged appropriately
159 lines
4.3 KiB
React
159 lines
4.3 KiB
React
// Components
|
|
import { Stack, Typography, Tab } from "@mui/material";
|
|
import Breadcrumbs from "@/Components/v1/Breadcrumbs/index.jsx";
|
|
import MonitorDetailsControlHeader from "@/Components/v1/MonitorDetailsControlHeader/index.jsx";
|
|
import MonitorTimeFrameHeader from "@/Components/v1/MonitorTimeFrameHeader/index.jsx";
|
|
import StatusBoxes from "./Components/StatusBoxes/index.jsx";
|
|
import GaugeBoxes from "./Components/GaugeBoxes/index.jsx";
|
|
import AreaChartBoxes from "./Components/AreaChartBoxes/index.jsx";
|
|
import GenericFallback from "@/Components/v1/GenericFallback/index.jsx";
|
|
import NetworkStats from "./Components/NetworkStats/index.jsx";
|
|
import ContainerStats from "./Components/ContainerStats/index.jsx";
|
|
import CustomTabList from "@/Components/v1/Tab/index.jsx";
|
|
import TabContext from "@mui/lab/TabContext";
|
|
|
|
// Utils
|
|
import { useTheme } from "@emotion/react";
|
|
import { useIsAdmin } from "@/Hooks/useIsAdmin.js";
|
|
import { useFetchHardwareMonitorById } from "../../../Hooks/monitorHooks.js";
|
|
import { useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useParams } from "react-router-dom";
|
|
|
|
// Constants
|
|
const BREADCRUMBS = [
|
|
{ name: "infrastructure monitors", path: "/infrastructure" },
|
|
{ name: "details", path: "" },
|
|
];
|
|
const InfrastructureDetails = () => {
|
|
// Local state
|
|
const [dateRange, setDateRange] = useState("recent");
|
|
const [trigger, setTrigger] = useState(false);
|
|
const [tab, setTab] = useState("details");
|
|
|
|
// Utils
|
|
const theme = useTheme();
|
|
const { monitorId } = useParams();
|
|
const { t } = useTranslation();
|
|
const isAdmin = useIsAdmin();
|
|
|
|
const [monitor, isLoading, networkError] = useFetchHardwareMonitorById({
|
|
monitorId,
|
|
dateRange,
|
|
updateTrigger: trigger,
|
|
});
|
|
|
|
const triggerUpdate = () => {
|
|
setTrigger(!trigger);
|
|
};
|
|
|
|
if (networkError === true) {
|
|
return (
|
|
<GenericFallback>
|
|
<Typography
|
|
variant="h1"
|
|
marginY={theme.spacing(4)}
|
|
color={theme.palette.primary.contrastTextTertiary}
|
|
>
|
|
{t("common.toasts.networkError")}
|
|
</Typography>
|
|
<Typography>{t("common.toasts.checkConnection")}</Typography>
|
|
</GenericFallback>
|
|
);
|
|
}
|
|
|
|
if (!isLoading && monitor?.stats?.checks?.length === 0) {
|
|
return (
|
|
<Stack gap={theme.spacing(10)}>
|
|
<Breadcrumbs list={BREADCRUMBS} />
|
|
<MonitorDetailsControlHeader
|
|
path={"infrastructure"}
|
|
isLoading={isLoading}
|
|
isAdmin={isAdmin}
|
|
monitor={monitor}
|
|
triggerUpdate={triggerUpdate}
|
|
/>
|
|
<GenericFallback>
|
|
<Typography>{t("distributedUptimeDetailsNoMonitorHistory")}</Typography>
|
|
</GenericFallback>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Stack gap={theme.spacing(10)}>
|
|
<Breadcrumbs list={BREADCRUMBS} />
|
|
<MonitorDetailsControlHeader
|
|
path={"infrastructure"}
|
|
isLoading={isLoading}
|
|
isAdmin={isAdmin}
|
|
monitor={monitor}
|
|
triggerUpdate={triggerUpdate}
|
|
/>
|
|
<TabContext value={tab}>
|
|
<CustomTabList
|
|
value={tab}
|
|
onChange={(e, v) => setTab(v)}
|
|
>
|
|
<Tab
|
|
label={t("details")}
|
|
value="details"
|
|
/>
|
|
<Tab
|
|
label={t("network")}
|
|
value="network"
|
|
/>
|
|
{monitor?.enableProxmoxContainers && (
|
|
<Tab
|
|
label={t("containers")}
|
|
value="containers"
|
|
/>
|
|
)}
|
|
</CustomTabList>
|
|
{tab === "details" && (
|
|
<>
|
|
<StatusBoxes
|
|
shouldRender={!isLoading}
|
|
monitor={monitor}
|
|
/>
|
|
<GaugeBoxes
|
|
isLoading={isLoading}
|
|
monitor={monitor}
|
|
/>
|
|
<MonitorTimeFrameHeader
|
|
isLoading={isLoading}
|
|
dateRange={dateRange}
|
|
setDateRange={setDateRange}
|
|
/>
|
|
<AreaChartBoxes
|
|
shouldRender={!isLoading}
|
|
monitor={monitor}
|
|
dateRange={dateRange}
|
|
/>
|
|
</>
|
|
)}
|
|
{tab === "network" && (
|
|
<NetworkStats
|
|
net={monitor?.stats?.aggregateData?.latestCheck?.net || []}
|
|
isLoading={isLoading}
|
|
checks={monitor?.stats?.checks}
|
|
dateRange={dateRange}
|
|
setDateRange={setDateRange}
|
|
/>
|
|
)}
|
|
{tab === "containers" && monitor?.enableProxmoxContainers && (
|
|
<ContainerStats
|
|
containers={monitor?.stats?.aggregateData?.latestCheck?.containers || []}
|
|
isLoading={isLoading}
|
|
checks={monitor?.stats?.checks}
|
|
dateRange={dateRange}
|
|
setDateRange={setDateRange}
|
|
/>
|
|
)}
|
|
</TabContext>
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export default InfrastructureDetails;
|