Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3947681a2 | ||
|
|
01084108b0 | ||
|
|
e9da602af6 |
No files matched your search
@@ -154,7 +154,10 @@ export const InfrastructureTooltip = ({
|
||||
yKey,
|
||||
yIdx = -1,
|
||||
yLabel,
|
||||
yKey2,
|
||||
yLabel2,
|
||||
dotColor,
|
||||
dotColor2,
|
||||
dateRange,
|
||||
formatter = getFormattedPercentage,
|
||||
}) => {
|
||||
@@ -216,7 +219,37 @@ export const InfrastructureTooltip = ({
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
{/* Display original value */}
|
||||
{yKey2 && yLabel2 && (
|
||||
<Box mt={theme.spacing(1)}>
|
||||
<Box
|
||||
display="inline-block"
|
||||
width={theme.spacing(4)}
|
||||
height={theme.spacing(4)}
|
||||
backgroundColor={dotColor2 || dotColor}
|
||||
sx={{ borderRadius: "50%" }}
|
||||
/>
|
||||
<Stack
|
||||
display="inline-flex"
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
ml={theme.spacing(3)}
|
||||
sx={{
|
||||
"& span": {
|
||||
color: theme.palette.primary.contrastTextTertiary,
|
||||
fontSize: 11,
|
||||
fontWeight: 500,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{ opacity: 0.8 }}
|
||||
>
|
||||
{`${yLabel2} ${formatter(payload[0].payload[yKey2])}`}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -234,7 +267,10 @@ InfrastructureTooltip.propTypes = {
|
||||
yKey: PropTypes.string,
|
||||
yIdx: PropTypes.number,
|
||||
yLabel: PropTypes.string,
|
||||
yKey2: PropTypes.string,
|
||||
yLabel2: PropTypes.string,
|
||||
dotColor: PropTypes.string,
|
||||
dotColor2: PropTypes.string,
|
||||
dateRange: PropTypes.string,
|
||||
};
|
||||
|
||||
|
||||
@@ -386,6 +386,7 @@ export const useUpdateMonitor = () => {
|
||||
thresholds: monitor.thresholds,
|
||||
secret: monitor.secret,
|
||||
selectedDisks: monitor.selectedDisks,
|
||||
enableProxmoxContainers: monitor.enableProxmoxContainers,
|
||||
}),
|
||||
};
|
||||
await networkService.updateMonitor({
|
||||
|
||||
@@ -18,6 +18,7 @@ const useInfrastructureMonitorForm = () => {
|
||||
usage_temperature: "",
|
||||
secret: "",
|
||||
selectedDisks: [],
|
||||
enableProxmoxContainers: false,
|
||||
});
|
||||
|
||||
const onChangeForm = (name, value) => {
|
||||
@@ -50,6 +51,7 @@ const useInfrastructureMonitorForm = () => {
|
||||
usage_temperature: gt.temperature !== undefined ? gt.temperature.toString() : "",
|
||||
secret: "",
|
||||
selectedDisks: [],
|
||||
enableProxmoxContainers: false,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
@@ -84,6 +86,7 @@ const useInfrastructureMonitorForm = () => {
|
||||
: "",
|
||||
secret: monitor.secret || "",
|
||||
selectedDisks: monitor.selectedDisks || [],
|
||||
enableProxmoxContainers: monitor.enableProxmoxContainers ?? false,
|
||||
}));
|
||||
}, []);
|
||||
return {
|
||||
|
||||
@@ -31,6 +31,7 @@ const useInfrastructureSubmit = () => {
|
||||
: {}),
|
||||
secret: infrastructureMonitor.secret,
|
||||
selectedDisks: infrastructureMonitor.selectedDisks,
|
||||
enableProxmoxContainers: infrastructureMonitor.enableProxmoxContainers,
|
||||
};
|
||||
return form;
|
||||
};
|
||||
@@ -50,6 +51,7 @@ const useInfrastructureSubmit = () => {
|
||||
temperature,
|
||||
usage_temperature,
|
||||
selectedDisks,
|
||||
enableProxmoxContainers,
|
||||
...rest
|
||||
} = form;
|
||||
|
||||
@@ -67,6 +69,7 @@ const useInfrastructureSubmit = () => {
|
||||
type: "hardware",
|
||||
notifications: infrastructureMonitor.notifications,
|
||||
selectedDisks,
|
||||
enableProxmoxContainers,
|
||||
thresholds,
|
||||
};
|
||||
// Handle create or update
|
||||
|
||||
@@ -5,7 +5,7 @@ import FieldWrapper from "@/Components/v1/Inputs/FieldWrapper/index.jsx";
|
||||
import Link from "@/Components/v1/Link/index.jsx";
|
||||
import Select from "@/Components/v1/Inputs/Select/index.jsx";
|
||||
import TextInput from "@/Components/v1/Inputs/TextInput/index.jsx";
|
||||
import { Box, Stack, Typography, Button, ButtonGroup } from "@mui/material";
|
||||
import { Box, Stack, Typography, Button, ButtonGroup, Switch, FormControlLabel } from "@mui/material";
|
||||
import { HttpAdornment } from "@/Components/v1/Inputs/TextInput/Adornments/index.jsx";
|
||||
import MonitorStatusHeader from "./Components/MonitorStatusHeader.jsx";
|
||||
import MonitorActionButtons from "./Components/MonitorActionButtons.jsx";
|
||||
@@ -368,6 +368,34 @@ const CreateInfrastructureMonitor = () => {
|
||||
/>
|
||||
)}
|
||||
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("proxmoxContainerMonitoring")}
|
||||
</Typography>
|
||||
<Typography component="p">
|
||||
{t("proxmoxContainerMonitoringDescription")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack>
|
||||
<FormControlLabel
|
||||
sx={{ marginLeft: theme.spacing(0) }}
|
||||
control={
|
||||
<Switch
|
||||
name="enableProxmoxContainers"
|
||||
checked={infrastructureMonitor.enableProxmoxContainers}
|
||||
onChange={handleCheckboxChange}
|
||||
sx={{ mr: theme.spacing(2) }}
|
||||
/>
|
||||
}
|
||||
label={t("enableProxmoxContainers")}
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import InfraAreaChart from "../AreaChartBoxes/InfraAreaChart.jsx";
|
||||
|
||||
import {
|
||||
TzTick,
|
||||
InfrastructureTooltip,
|
||||
NetworkTick,
|
||||
PercentTick,
|
||||
} from "@/Components/v1/Charts/Utils/chartUtils.jsx";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHardwareUtils } from "../../Hooks/useHardwareUtils.jsx";
|
||||
|
||||
const ContainerCharts = ({ containerData, dateRange }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { formatBytesPerSecondString } = useHardwareUtils();
|
||||
|
||||
if (!containerData?.length) {
|
||||
return <Typography>{t("noContainerStatsAvailable")}</Typography>;
|
||||
}
|
||||
|
||||
const formatPercentage = (value) => `${(value * 100).toFixed(1)}%`;
|
||||
|
||||
const configs = [
|
||||
{
|
||||
type: "container-cpu",
|
||||
data: containerData,
|
||||
dataKeys: ["cpuUsage"],
|
||||
heading: t("cpuUsage"),
|
||||
strokeColor: theme.palette.success.main,
|
||||
gradientStartColor: theme.palette.success.main,
|
||||
yLabel: t("usage"),
|
||||
xTick: <TzTick dateRange={dateRange} />,
|
||||
yTick: <PercentTick />,
|
||||
toolTip: (
|
||||
<InfrastructureTooltip
|
||||
dotColor={theme.palette.success.main}
|
||||
yKey={"cpuUsage"}
|
||||
yLabel={t("cpuUsage") + ": "}
|
||||
dateRange={dateRange}
|
||||
formatter={formatPercentage}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "container-memory",
|
||||
data: containerData,
|
||||
dataKeys: ["memoryUsage"],
|
||||
heading: t("memoryUsage"),
|
||||
strokeColor: theme.palette.accent.main,
|
||||
gradientStartColor: theme.palette.accent.main,
|
||||
yLabel: t("usage"),
|
||||
xTick: <TzTick dateRange={dateRange} />,
|
||||
yTick: <PercentTick />,
|
||||
toolTip: (
|
||||
<InfrastructureTooltip
|
||||
dotColor={theme.palette.accent.main}
|
||||
yKey={"memoryUsage"}
|
||||
yLabel={t("memoryUsage") + ": "}
|
||||
dateRange={dateRange}
|
||||
formatter={formatPercentage}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "container-disk-io",
|
||||
data: containerData,
|
||||
dataKeys: ["diskRead", "diskWrite"],
|
||||
heading: t("diskIO"),
|
||||
// Don't pass strokeColor - let chart use distinct colors from palette
|
||||
yLabel: t("rate"),
|
||||
xTick: <TzTick dateRange={dateRange} />,
|
||||
yTick: <NetworkTick formatter={formatBytesPerSecondString} />,
|
||||
toolTip: (
|
||||
<InfrastructureTooltip
|
||||
dotColor={"#3182bd"}
|
||||
yKey={"diskRead"}
|
||||
yLabel={t("diskRead") + ": "}
|
||||
dotColor2={"#6baed6"}
|
||||
yKey2={"diskWrite"}
|
||||
yLabel2={t("diskWrite") + ": "}
|
||||
dateRange={dateRange}
|
||||
formatter={formatBytesPerSecondString}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "container-network-io",
|
||||
data: containerData,
|
||||
dataKeys: ["netIn", "netOut"],
|
||||
heading: t("networkIO"),
|
||||
// Don't pass strokeColor - let chart use distinct colors from palette
|
||||
yLabel: t("rate"),
|
||||
xTick: <TzTick dateRange={dateRange} />,
|
||||
yTick: <NetworkTick formatter={formatBytesPerSecondString} />,
|
||||
toolTip: (
|
||||
<InfrastructureTooltip
|
||||
dotColor={"#3182bd"}
|
||||
yKey={"netIn"}
|
||||
yLabel={t("networkIn") + ": "}
|
||||
dotColor2={"#6baed6"}
|
||||
yKey2={"netOut"}
|
||||
yLabel2={t("networkOut") + ": "}
|
||||
dateRange={dateRange}
|
||||
formatter={formatBytesPerSecondString}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction={"row"}
|
||||
gap={theme.spacing(8)}
|
||||
flexWrap="wrap"
|
||||
sx={{
|
||||
"& > *": {
|
||||
flexBasis: `calc(50% - ${theme.spacing(8)})`,
|
||||
maxWidth: `calc(50% - ${theme.spacing(8)})`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{configs.map((config) => (
|
||||
<InfraAreaChart
|
||||
key={config.type}
|
||||
config={config}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
ContainerCharts.propTypes = {
|
||||
containerData: PropTypes.array.isRequired,
|
||||
dateRange: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default ContainerCharts;
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
import PropTypes from "prop-types";
|
||||
import StatusBoxes from "@/Components/v1/StatusBoxes/index.jsx";
|
||||
import StatBox from "@/Components/v1/StatBox/index.jsx";
|
||||
import { Typography } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHardwareUtils } from "../../Hooks/useHardwareUtils.jsx";
|
||||
|
||||
const formatUptime = (seconds) => {
|
||||
if (!seconds || seconds === 0) return "0s";
|
||||
|
||||
const days = Math.floor(seconds / 86400);
|
||||
const hours = Math.floor((seconds % 86400) / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
|
||||
if (days > 0) {
|
||||
return `${days}d ${hours}h`;
|
||||
} else if (hours > 0) {
|
||||
return `${hours}h ${minutes}m`;
|
||||
} else if (minutes > 0) {
|
||||
return `${minutes}m`;
|
||||
}
|
||||
return `${Math.floor(seconds)}s`;
|
||||
};
|
||||
|
||||
const formatPercentage = (value) => {
|
||||
if (value === undefined || value === null) return "0%";
|
||||
return `${(value * 100).toFixed(1)}%`;
|
||||
};
|
||||
|
||||
// Map container status to Checkmate monitor status for theming
|
||||
const containerStatusToMonitorStatus = (status) => {
|
||||
if (status === "running") return "up";
|
||||
if (status === "stopped") return "down";
|
||||
return "pending";
|
||||
};
|
||||
|
||||
const ContainerStatBoxes = ({ shouldRender, container }) => {
|
||||
const { t } = useTranslation();
|
||||
const { formatBytes } = useHardwareUtils();
|
||||
|
||||
if (!container) {
|
||||
return <Typography>{t("noContainerSelected")}</Typography>;
|
||||
}
|
||||
|
||||
const monitorStatus = containerStatusToMonitorStatus(container.status);
|
||||
|
||||
return (
|
||||
<StatusBoxes
|
||||
shouldRender={shouldRender}
|
||||
flexWrap="wrap"
|
||||
>
|
||||
<StatBox
|
||||
gradient={true}
|
||||
status={monitorStatus}
|
||||
heading={t("status")}
|
||||
subHeading={container.status || "unknown"}
|
||||
/>
|
||||
<StatBox
|
||||
heading={t("type")}
|
||||
subHeading={
|
||||
<Typography component="span">
|
||||
{(container.type || "lxc").toUpperCase()}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
<StatBox
|
||||
heading={t("uptime")}
|
||||
subHeading={formatUptime(container.uptime)}
|
||||
/>
|
||||
<StatBox
|
||||
heading={t("cpuUsage")}
|
||||
subHeading={
|
||||
<>
|
||||
{formatPercentage(container.cpu_usage)}
|
||||
<Typography component="span">
|
||||
({container.cpu_cores || 0} {t("cores")})
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<StatBox
|
||||
heading={t("memoryUsage")}
|
||||
subHeading={
|
||||
<>
|
||||
{formatBytes(container.memory_used)}
|
||||
<Typography component="span"> / </Typography>
|
||||
{formatBytes(container.memory_total)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<StatBox
|
||||
heading={t("diskUsage")}
|
||||
subHeading={
|
||||
<>
|
||||
{formatBytes(container.disk_used)}
|
||||
<Typography component="span"> / </Typography>
|
||||
{formatBytes(container.disk_total)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</StatusBoxes>
|
||||
);
|
||||
};
|
||||
|
||||
ContainerStatBoxes.propTypes = {
|
||||
shouldRender: PropTypes.bool.isRequired,
|
||||
container: PropTypes.shape({
|
||||
vmid: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
name: PropTypes.string,
|
||||
node: PropTypes.string,
|
||||
status: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
uptime: PropTypes.number,
|
||||
cpu_cores: PropTypes.number,
|
||||
cpu_usage: PropTypes.number,
|
||||
memory_used: PropTypes.number,
|
||||
memory_total: PropTypes.number,
|
||||
memory_usage: PropTypes.number,
|
||||
swap_used: PropTypes.number,
|
||||
swap_total: PropTypes.number,
|
||||
disk_used: PropTypes.number,
|
||||
disk_total: PropTypes.number,
|
||||
disk_read: PropTypes.number,
|
||||
disk_write: PropTypes.number,
|
||||
network_in: PropTypes.number,
|
||||
network_out: PropTypes.number,
|
||||
}),
|
||||
};
|
||||
|
||||
export default ContainerStatBoxes;
|
||||
@@ -0,0 +1,125 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Select from "@/Components/v1/Inputs/Select/index.jsx";
|
||||
import ContainerStatBoxes from "./ContainerStatBoxes.jsx";
|
||||
import ContainerCharts from "./ContainerCharts.jsx";
|
||||
import MonitorTimeFrameHeader from "@/Components/v1/MonitorTimeFrameHeader/index.jsx";
|
||||
|
||||
const getAvailableContainers = (containers) => {
|
||||
return (containers || [])
|
||||
.filter((c) => c.vmid && c.name)
|
||||
.map((c) => ({
|
||||
vmid: String(c.vmid),
|
||||
name: c.name,
|
||||
node: c.node,
|
||||
status: c.status,
|
||||
type: c.type,
|
||||
}));
|
||||
};
|
||||
|
||||
const getContainerData = (checks, vmid) => {
|
||||
if (!vmid) return [];
|
||||
|
||||
return (checks || [])
|
||||
.map((check) => {
|
||||
const container = (check.containers || []).find(
|
||||
(c) => String(c.vmid) === String(vmid)
|
||||
);
|
||||
if (!container) return null;
|
||||
return {
|
||||
_id: check._id,
|
||||
cpuUsage: container.avgCpuUsage ?? 0,
|
||||
memoryUsage: container.avgMemoryUsage ?? 0,
|
||||
diskRead: container.deltaDiskRead ?? 0,
|
||||
diskWrite: container.deltaDiskWrite ?? 0,
|
||||
netIn: container.deltaNetIn ?? 0,
|
||||
netOut: container.deltaNetOut ?? 0,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
};
|
||||
|
||||
const ContainerStats = ({ containers, checks, isLoading, dateRange, setDateRange }) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
const availableContainers = getAvailableContainers(containers);
|
||||
const [selectedContainer, setSelectedContainer] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (availableContainers.length > 0 && !selectedContainer) {
|
||||
setSelectedContainer(availableContainers[0].vmid);
|
||||
}
|
||||
}, [availableContainers, selectedContainer]);
|
||||
|
||||
const containerData = getContainerData(checks, selectedContainer);
|
||||
const currentContainer = containers?.find(
|
||||
(c) => String(c.vmid) === String(selectedContainer)
|
||||
);
|
||||
|
||||
if (!containers?.length) {
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
minHeight={200}
|
||||
>
|
||||
<Typography color={theme.palette.primary.contrastTextTertiary}>
|
||||
{t("noContainersAvailable")}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContainerStatBoxes
|
||||
shouldRender={!isLoading}
|
||||
container={currentContainer}
|
||||
/>
|
||||
<Box
|
||||
display="flex"
|
||||
justifyContent="space-between"
|
||||
alignItems="flex-end"
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
{availableContainers.length > 0 && (
|
||||
<Select
|
||||
name="container"
|
||||
label={t("container")}
|
||||
value={selectedContainer}
|
||||
onChange={(e) => setSelectedContainer(e.target.value)}
|
||||
items={availableContainers.map((c) => ({
|
||||
_id: c.vmid,
|
||||
name: `${c.name} (${c.vmid}) - ${c.node}`,
|
||||
}))}
|
||||
sx={{ minWidth: 250 }}
|
||||
/>
|
||||
)}
|
||||
<MonitorTimeFrameHeader
|
||||
isLoading={isLoading}
|
||||
dateRange={dateRange}
|
||||
setDateRange={setDateRange}
|
||||
/>
|
||||
</Box>
|
||||
<ContainerCharts
|
||||
containerData={containerData}
|
||||
dateRange={dateRange}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ContainerStats.propTypes = {
|
||||
containers: PropTypes.array,
|
||||
checks: PropTypes.array,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
dateRange: PropTypes.string.isRequired,
|
||||
setDateRange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ContainerStats;
|
||||
@@ -8,6 +8,7 @@ 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";
|
||||
|
||||
@@ -102,6 +103,12 @@ const InfrastructureDetails = () => {
|
||||
label={t("network")}
|
||||
value="network"
|
||||
/>
|
||||
{monitor?.enableProxmoxContainers && (
|
||||
<Tab
|
||||
label={t("containers")}
|
||||
value="containers"
|
||||
/>
|
||||
)}
|
||||
</CustomTabList>
|
||||
{tab === "details" && (
|
||||
<>
|
||||
@@ -134,6 +141,15 @@ const InfrastructureDetails = () => {
|
||||
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>
|
||||
);
|
||||
|
||||
@@ -450,6 +450,7 @@ const infrastructureMonitorValidation = joi.object({
|
||||
}),
|
||||
notifications: joi.array().items(joi.string()),
|
||||
selectedDisks: joi.array().items(joi.string()).optional(),
|
||||
enableProxmoxContainers: joi.boolean(),
|
||||
});
|
||||
|
||||
const notificationValidation = joi.object({
|
||||
|
||||
@@ -85,7 +85,9 @@
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
@@ -930,7 +932,9 @@
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -985,28 +989,36 @@
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -1104,5 +1116,19 @@
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
"selectInterface": "",
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -85,7 +85,9 @@
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
@@ -930,7 +932,9 @@
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -985,28 +989,36 @@
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -1104,5 +1116,19 @@
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
"selectInterface": "",
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -1136,5 +1136,19 @@
|
||||
"disk_selection_info": "Momentan wurde keine Festplatte erkannt.",
|
||||
"disk_selection_description": "Wählen Sie die spezifischen Festplatten aus, die Sie überwachen möchten."
|
||||
}
|
||||
}
|
||||
},
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -181,6 +181,9 @@
|
||||
"infrastructureServerUrlLabel": "Server URL",
|
||||
"infrastructureDisplayNameLabel": "Display name",
|
||||
"infrastructureAuthorizationSecretLabel": "Authorization secret",
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"gb": "GB",
|
||||
"mb": "MB",
|
||||
"mem": "Mem",
|
||||
@@ -1135,6 +1138,7 @@
|
||||
"dataRate": "Data Rate",
|
||||
"dataReceived": "Data Received",
|
||||
"dataSent": "Data Sent",
|
||||
"containers": "Containers",
|
||||
"details": "Details",
|
||||
"drops": "Drops",
|
||||
"errors": "Errors",
|
||||
@@ -1147,6 +1151,16 @@
|
||||
"networkErrors": "Network Errors",
|
||||
"networkInterface": "Network Interface",
|
||||
"noNetworkStatsAvailable": "No network stats available.",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out",
|
||||
"packetsPerSecond": "Packets per second",
|
||||
"packetsReceived": "Packets Received",
|
||||
"packetsReceivedRate": "Packets Received Rate",
|
||||
|
||||
@@ -85,7 +85,9 @@
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
@@ -930,7 +932,9 @@
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -985,28 +989,36 @@
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -1104,5 +1116,19 @@
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
"selectInterface": "",
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -85,7 +85,9 @@
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "",
|
||||
@@ -930,7 +932,9 @@
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -985,28 +989,36 @@
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -1104,5 +1116,19 @@
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
"selectInterface": "",
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -1136,5 +1136,19 @@
|
||||
"disk_selection_info": "Aucun disque détecté pour le moment.",
|
||||
"disk_selection_description": "Sélectionnez les disques spécifiques que vous souhaitez surveiller."
|
||||
}
|
||||
}
|
||||
},
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -934,7 +934,9 @@
|
||||
"generalSettings": "一般設定",
|
||||
"contents": "コンテンツ",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -989,28 +991,36 @@
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -1115,5 +1125,19 @@
|
||||
"disk_selection_info": "現在ディスクが検出されていません。",
|
||||
"disk_selection_description": "監視したい特定のディスクを選択してください。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -1136,5 +1136,19 @@
|
||||
"disk_selection_info": "Nenhum disco detectado no momento.",
|
||||
"disk_selection_description": "Selecione os discos específicos que você deseja monitorar."
|
||||
}
|
||||
}
|
||||
},
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -1136,5 +1136,19 @@
|
||||
"disk_selection_info": "На данный момент диск не обнаружен.",
|
||||
"disk_selection_description": "Выберите конкретные диски, которые вы хотите отслеживать."
|
||||
}
|
||||
}
|
||||
},
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -1136,5 +1136,19 @@
|
||||
"disk_selection_info": "ไม่พบดิสก์ในขณะนี้",
|
||||
"disk_selection_description": "เลือกดิสก์ที่คุณต้องการตรวจสอบ"
|
||||
}
|
||||
}
|
||||
},
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -85,7 +85,9 @@
|
||||
"networkError": "Ağ hatası oluştu",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"actionButton": ""
|
||||
},
|
||||
"createButton": "Bildirim kanalı oluştur",
|
||||
@@ -930,7 +932,9 @@
|
||||
"generalSettings": "Genel ayarlar",
|
||||
"contents": "İçerikler",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -985,28 +989,36 @@
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
@@ -1104,5 +1116,19 @@
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
"selectInterface": "",
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -86,7 +86,9 @@
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""]
|
||||
"checks": [
|
||||
""
|
||||
]
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
@@ -834,5 +836,19 @@
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -86,7 +86,9 @@
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""]
|
||||
"checks": [
|
||||
""
|
||||
]
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
@@ -834,5 +836,19 @@
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -1004,14 +1004,22 @@
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": ["标记维护时段", "消除所有误解", "停止在维护窗口内发送警报"],
|
||||
"checks": [
|
||||
"标记维护时段",
|
||||
"消除所有误解",
|
||||
"停止在维护窗口内发送警报"
|
||||
],
|
||||
"title": "维护时段用于:",
|
||||
"actionButton": "让我们创建您的第一个维护时段!"
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": ["报告页面的用户体验", "帮助分析网页速度", "就如何改进页面提出建议"],
|
||||
"checks": [
|
||||
"报告页面的用户体验",
|
||||
"帮助分析网页速度",
|
||||
"就如何改进页面提出建议"
|
||||
],
|
||||
"title": "PageSpeed 监视器用于:",
|
||||
"actionButton": "让我们创建您的第一个 PageSpeed 监视器!"
|
||||
}
|
||||
@@ -1128,5 +1136,19 @@
|
||||
"disk_selection_info": "目前没有检测到磁盘。",
|
||||
"disk_selection_description": "选择您要监控的特定磁盘。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -934,7 +934,9 @@
|
||||
"generalSettings": "",
|
||||
"contents": "內容",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": "讓我們建立您的第一個狀態頁面!"
|
||||
}
|
||||
@@ -989,28 +991,40 @@
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": ["追蹤伺服器的效能", "識別瓶頸並優化資源使用", "透過即時監控確保可靠性"],
|
||||
"checks": [
|
||||
"追蹤伺服器的效能",
|
||||
"識別瓶頸並優化資源使用",
|
||||
"透過即時監控確保可靠性"
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": "讓我們建立您的第一個基礎設施監視器!"
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": ["標記您的維護期間", "消除任何誤解", "在維護時段停止發送警報"],
|
||||
"checks": [
|
||||
"標記您的維護期間",
|
||||
"消除任何誤解",
|
||||
"在維護時段停止發送警報"
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": "讓我們建立您的第一個維護時段!"
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": "讓我們建立您的第一個 PageSpeed 監視器!"
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
""
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": "讓我們建立您的第一個正常運行時間監視器!"
|
||||
}
|
||||
@@ -1108,5 +1122,19 @@
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
"selectInterface": "",
|
||||
"proxmoxContainerMonitoring": "Proxmox container monitoring",
|
||||
"proxmoxContainerMonitoringDescription": "Enable monitoring of Proxmox LXC containers on this infrastructure node. Requires Capture agent to be configured with Proxmox API credentials.",
|
||||
"enableProxmoxContainers": "Enable container monitoring",
|
||||
"noContainersAvailable": "No containers available.",
|
||||
"noContainerSelected": "No container selected.",
|
||||
"noContainerStatsAvailable": "No container stats available.",
|
||||
"container": "Container",
|
||||
"containers": "Containers",
|
||||
"diskIO": "Disk I/O",
|
||||
"networkIO": "Network I/O",
|
||||
"diskRead": "Disk read",
|
||||
"diskWrite": "Disk write",
|
||||
"networkIn": "Network in",
|
||||
"networkOut": "Network out"
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
Check,
|
||||
CheckAudits,
|
||||
CheckCaptureInfo,
|
||||
CheckContainerInfo,
|
||||
CheckCpuInfo,
|
||||
CheckDiskInfo,
|
||||
CheckErrorInfo,
|
||||
@@ -143,6 +144,31 @@ const networkInterfaceSchema = new Schema<CheckNetworkInterfaceInfo>(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const containerSchema = new Schema<CheckContainerInfo>(
|
||||
{
|
||||
vmid: { type: String, default: "" },
|
||||
name: { type: String, default: "" },
|
||||
node: { type: String, default: "" },
|
||||
status: { type: String, default: "" },
|
||||
type: { type: String, default: "lxc" },
|
||||
uptime: { type: Number, default: 0 },
|
||||
cpu_cores: { type: Number, default: 0 },
|
||||
cpu_usage: { type: Number, default: 0 },
|
||||
memory_used: { type: Number, default: 0 },
|
||||
memory_total: { type: Number, default: 0 },
|
||||
memory_usage: { type: Number, default: 0 },
|
||||
swap_used: { type: Number, default: 0 },
|
||||
swap_total: { type: Number, default: 0 },
|
||||
disk_used: { type: Number, default: 0 },
|
||||
disk_total: { type: Number, default: 0 },
|
||||
disk_read: { type: Number, default: 0 },
|
||||
disk_write: { type: Number, default: 0 },
|
||||
network_in: { type: Number, default: 0 },
|
||||
network_out: { type: Number, default: 0 },
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const lighthouseAuditSchema = new Schema<ILighthouseAudit>(
|
||||
{
|
||||
id: { type: String },
|
||||
@@ -256,6 +282,10 @@ const CheckSchema = new Schema<CheckDocument>(
|
||||
type: [networkInterfaceSchema],
|
||||
default: () => [],
|
||||
},
|
||||
containers: {
|
||||
type: [containerSchema],
|
||||
default: () => [],
|
||||
},
|
||||
accessibility: {
|
||||
type: Number,
|
||||
},
|
||||
|
||||
@@ -80,6 +80,10 @@ const MonitorSchema = new Schema<MonitorDocument>(
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
enableProxmoxContainers: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
jsonPath: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
Check,
|
||||
CheckAudits,
|
||||
CheckCaptureInfo,
|
||||
CheckContainerInfo,
|
||||
CheckCpuInfo,
|
||||
CheckDiskInfo,
|
||||
CheckErrorInfo,
|
||||
@@ -137,6 +138,29 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
fifo_out: iface?.fifo_out ?? 0,
|
||||
}));
|
||||
|
||||
const mapContainers = (containers?: CheckContainerInfo[]): CheckContainerInfo[] =>
|
||||
(containers ?? []).map((container) => ({
|
||||
vmid: container?.vmid ?? "",
|
||||
name: container?.name ?? "",
|
||||
node: container?.node ?? "",
|
||||
status: container?.status ?? "",
|
||||
type: container?.type ?? "lxc",
|
||||
uptime: container?.uptime ?? 0,
|
||||
cpu_cores: container?.cpu_cores ?? 0,
|
||||
cpu_usage: container?.cpu_usage ?? 0,
|
||||
memory_used: container?.memory_used ?? 0,
|
||||
memory_total: container?.memory_total ?? 0,
|
||||
memory_usage: container?.memory_usage ?? 0,
|
||||
swap_used: container?.swap_used ?? 0,
|
||||
swap_total: container?.swap_total ?? 0,
|
||||
disk_used: container?.disk_used ?? 0,
|
||||
disk_total: container?.disk_total ?? 0,
|
||||
disk_read: container?.disk_read ?? 0,
|
||||
disk_write: container?.disk_write ?? 0,
|
||||
network_in: container?.network_in ?? 0,
|
||||
network_out: container?.network_out ?? 0,
|
||||
}));
|
||||
|
||||
const mapAudits = (audits?: CheckAudits): CheckAudits | undefined => {
|
||||
if (!audits) {
|
||||
return undefined;
|
||||
@@ -174,6 +198,7 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
errors: mapErrors(doc.errors),
|
||||
capture: mapCapture(doc.capture),
|
||||
net: mapNet(doc.net),
|
||||
containers: mapContainers(doc.containers),
|
||||
accessibility: doc.accessibility,
|
||||
bestPractices: doc.bestPractices,
|
||||
seo: doc.seo,
|
||||
@@ -374,6 +399,24 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
deltaFifoIn: iface?.deltaFifoIn ?? 0,
|
||||
deltaFifoOut: iface?.deltaFifoOut ?? 0,
|
||||
})),
|
||||
containers: (metric.containers ?? []).map((container: { [key: string]: number | string | undefined }) => ({
|
||||
vmid: container?.vmid ?? "",
|
||||
name: container?.name ?? "",
|
||||
node: container?.node ?? "",
|
||||
status: container?.status ?? "",
|
||||
type: container?.type ?? "",
|
||||
cpu_cores: container?.cpu_cores ?? 0,
|
||||
avgCpuUsage: container?.avgCpuUsage ?? 0,
|
||||
avgMemoryUsage: container?.avgMemoryUsage ?? 0,
|
||||
memoryTotal: container?.memoryTotal ?? 0,
|
||||
swapTotal: container?.swapTotal ?? 0,
|
||||
diskTotal: container?.diskTotal ?? 0,
|
||||
diskUsed: container?.diskUsed ?? 0,
|
||||
deltaDiskRead: container?.deltaDiskRead ?? 0,
|
||||
deltaDiskWrite: container?.deltaDiskWrite ?? 0,
|
||||
deltaNetIn: container?.deltaNetIn ?? 0,
|
||||
deltaNetOut: container?.deltaNetOut ?? 0,
|
||||
})),
|
||||
}));
|
||||
|
||||
return {
|
||||
@@ -451,6 +494,7 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
avgTemperatures: { $push: { $ifNull: ["$cpu.temperature", [0]] } },
|
||||
disks: { $push: "$disk" },
|
||||
net: { $push: "$net" },
|
||||
containers: { $push: "$containers" },
|
||||
updatedAts: { $push: "$updatedAt" },
|
||||
sampleDoc: { $first: "$$ROOT" },
|
||||
},
|
||||
@@ -570,6 +614,66 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
},
|
||||
},
|
||||
},
|
||||
containers: {
|
||||
$map: {
|
||||
input: { $range: [0, { $size: { $ifNull: ["$sampleDoc.containers", []] } }] },
|
||||
as: "cIdx",
|
||||
in: {
|
||||
vmid: { $arrayElemAt: ["$sampleDoc.containers.vmid", "$$cIdx"] },
|
||||
name: { $arrayElemAt: ["$sampleDoc.containers.name", "$$cIdx"] },
|
||||
node: { $arrayElemAt: ["$sampleDoc.containers.node", "$$cIdx"] },
|
||||
status: { $arrayElemAt: ["$sampleDoc.containers.status", "$$cIdx"] },
|
||||
type: { $arrayElemAt: ["$sampleDoc.containers.type", "$$cIdx"] },
|
||||
cpu_cores: { $arrayElemAt: ["$sampleDoc.containers.cpu_cores", "$$cIdx"] },
|
||||
avgCpuUsage: { $avg: { $map: { input: "$containers", as: "cA", in: { $arrayElemAt: ["$$cA.cpu_usage", "$$cIdx"] } } } },
|
||||
avgMemoryUsage: { $avg: { $map: { input: "$containers", as: "cA", in: { $arrayElemAt: ["$$cA.memory_usage", "$$cIdx"] } } } },
|
||||
memoryTotal: { $arrayElemAt: ["$sampleDoc.containers.memory_total", "$$cIdx"] },
|
||||
swapTotal: { $arrayElemAt: ["$sampleDoc.containers.swap_total", "$$cIdx"] },
|
||||
diskTotal: { $arrayElemAt: ["$sampleDoc.containers.disk_total", "$$cIdx"] },
|
||||
diskUsed: { $avg: { $map: { input: "$containers", as: "cA", in: { $arrayElemAt: ["$$cA.disk_used", "$$cIdx"] } } } },
|
||||
deltaDiskRead: {
|
||||
$let: {
|
||||
vars: {
|
||||
tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] },
|
||||
f: { $arrayElemAt: [{ $map: { input: { $first: "$containers" }, as: "c", in: "$$c.disk_read" } }, "$$cIdx"] },
|
||||
l: { $arrayElemAt: [{ $map: { input: { $last: "$containers" }, as: "c", in: "$$c.disk_read" } }, "$$cIdx"] },
|
||||
},
|
||||
in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] },
|
||||
},
|
||||
},
|
||||
deltaDiskWrite: {
|
||||
$let: {
|
||||
vars: {
|
||||
tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] },
|
||||
f: { $arrayElemAt: [{ $map: { input: { $first: "$containers" }, as: "c", in: "$$c.disk_write" } }, "$$cIdx"] },
|
||||
l: { $arrayElemAt: [{ $map: { input: { $last: "$containers" }, as: "c", in: "$$c.disk_write" } }, "$$cIdx"] },
|
||||
},
|
||||
in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] },
|
||||
},
|
||||
},
|
||||
deltaNetIn: {
|
||||
$let: {
|
||||
vars: {
|
||||
tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] },
|
||||
f: { $arrayElemAt: [{ $map: { input: { $first: "$containers" }, as: "c", in: "$$c.network_in" } }, "$$cIdx"] },
|
||||
l: { $arrayElemAt: [{ $map: { input: { $last: "$containers" }, as: "c", in: "$$c.network_in" } }, "$$cIdx"] },
|
||||
},
|
||||
in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] },
|
||||
},
|
||||
},
|
||||
deltaNetOut: {
|
||||
$let: {
|
||||
vars: {
|
||||
tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] },
|
||||
f: { $arrayElemAt: [{ $map: { input: { $first: "$containers" }, as: "c", in: "$$c.network_out" } }, "$$cIdx"] },
|
||||
l: { $arrayElemAt: [{ $map: { input: { $last: "$containers" }, as: "c", in: "$$c.network_out" } }, "$$cIdx"] },
|
||||
},
|
||||
in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ $sort: { _id: 1 } },
|
||||
|
||||
@@ -225,6 +225,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
|
||||
statusWindowThreshold: doc.statusWindowThreshold,
|
||||
type: doc.type,
|
||||
ignoreTlsErrors: doc.ignoreTlsErrors,
|
||||
enableProxmoxContainers: doc.enableProxmoxContainers,
|
||||
jsonPath: doc.jsonPath ?? undefined,
|
||||
expectedValue: doc.expectedValue ?? undefined,
|
||||
matchMethod: doc.matchMethod ?? undefined,
|
||||
|
||||
@@ -105,7 +105,7 @@ class CheckService {
|
||||
|
||||
if (type === "hardware") {
|
||||
const hardwarePayload = payload as HardwareStatusPayload | undefined;
|
||||
const { cpu, memory, disk, host, net } = hardwarePayload?.data ?? {};
|
||||
const { cpu, memory, disk, host, net, containers } = hardwarePayload?.data ?? {};
|
||||
const errorsSource = Array.isArray(hardwarePayload?.errors)
|
||||
? hardwarePayload?.errors
|
||||
: (hardwarePayload?.errors as { errors?: CheckErrorInfo[] } | undefined)?.errors;
|
||||
@@ -116,6 +116,7 @@ class CheckService {
|
||||
check.errors = errorsSource;
|
||||
check.capture = hardwarePayload?.capture;
|
||||
check.net = net;
|
||||
check.containers = containers;
|
||||
}
|
||||
return check;
|
||||
};
|
||||
|
||||
@@ -304,7 +304,8 @@ class NetworkService implements INetworkService {
|
||||
|
||||
Object.assign(httpResponse, {
|
||||
code: response.statusCode,
|
||||
status: response.ok,
|
||||
// 207 Multi-Status is used by Capture when some metrics fail - still a success
|
||||
status: response.ok || response.statusCode === 207,
|
||||
message: response.statusMessage ?? "",
|
||||
responseTime: response.timings.phases.total || 0,
|
||||
payload,
|
||||
@@ -424,7 +425,112 @@ class NetworkService implements INetworkService {
|
||||
|
||||
private async requestHardware(monitor: Monitor): Promise<MonitorStatusResponse> {
|
||||
try {
|
||||
return await this.requestHttp(monitor);
|
||||
// Fetch main metrics
|
||||
this.logger.info({
|
||||
message: "Fetching hardware metrics",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "requestHardware",
|
||||
details: { url: monitor.url },
|
||||
});
|
||||
const mainResponse = await this.requestHttp(monitor);
|
||||
|
||||
// If main request failed, return early
|
||||
if (!mainResponse.status) {
|
||||
this.logger.warn({
|
||||
message: "Main metrics request failed",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "requestHardware",
|
||||
details: { url: monitor.url, code: mainResponse.code, msg: mainResponse.message },
|
||||
});
|
||||
return mainResponse;
|
||||
}
|
||||
|
||||
// Only fetch Proxmox metrics if enabled for this monitor
|
||||
if (monitor?.enableProxmoxContainers === true) {
|
||||
try {
|
||||
const proxmoxUrl = this.buildProxmoxUrl(monitor.url);
|
||||
this.logger.info({
|
||||
message: "Attempting to fetch Proxmox metrics",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "requestHardware",
|
||||
details: { proxmoxUrl },
|
||||
});
|
||||
if (proxmoxUrl) {
|
||||
const proxmoxResponse = await this.requestHttp({
|
||||
...monitor,
|
||||
url: proxmoxUrl,
|
||||
});
|
||||
|
||||
// Proxmox response structure: { data: [...], errors: [...] | null, capture: {...} }
|
||||
const proxmoxPayload = proxmoxResponse.payload as {
|
||||
data?: unknown[] | null;
|
||||
errors?: Array<{ metric: string[]; err: string }> | null;
|
||||
};
|
||||
const mainPayload = mainResponse.payload as {
|
||||
data?: Record<string, unknown>;
|
||||
errors?: Array<{ metric: string[]; err: string }>;
|
||||
};
|
||||
|
||||
// Handle based on Capture's response scenarios:
|
||||
// 1. Not configured: data=[], errors=null -> log and skip
|
||||
// 2. Connection failed: data=null, errors=[...] -> merge errors
|
||||
// 3. Partial failure: data=[...], errors=[...] -> merge both
|
||||
// 4. Success: data=[...], errors=null -> merge data
|
||||
|
||||
if (proxmoxResponse.status && mainPayload?.data && typeof mainPayload.data === "object") {
|
||||
// Merge container data if available
|
||||
if (proxmoxPayload?.data && Array.isArray(proxmoxPayload.data)) {
|
||||
if (proxmoxPayload.data.length > 0) {
|
||||
(mainPayload.data as Record<string, unknown>).containers = proxmoxPayload.data;
|
||||
this.logger.info({
|
||||
message: "Successfully merged Proxmox container data",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "requestHardware",
|
||||
details: { containerCount: proxmoxPayload.data.length },
|
||||
});
|
||||
} else {
|
||||
this.logger.debug({
|
||||
message: "Proxmox returned empty container list (not configured or no containers running)",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "requestHardware",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Merge Proxmox errors into main errors array
|
||||
if (proxmoxPayload?.errors && Array.isArray(proxmoxPayload.errors) && proxmoxPayload.errors.length > 0) {
|
||||
if (!mainPayload.errors) {
|
||||
mainPayload.errors = [];
|
||||
}
|
||||
mainPayload.errors.push(...proxmoxPayload.errors);
|
||||
this.logger.warn({
|
||||
message: "Proxmox returned errors",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "requestHardware",
|
||||
details: { errors: proxmoxPayload.errors },
|
||||
});
|
||||
}
|
||||
} else if (!proxmoxResponse.status) {
|
||||
this.logger.warn({
|
||||
message: "Proxmox request failed",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "requestHardware",
|
||||
details: { status: proxmoxResponse.status, code: proxmoxResponse.code },
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (proxmoxErr: any) {
|
||||
// Log but don't fail - Proxmox is optional
|
||||
this.logger.warn({
|
||||
message: "Proxmox fetch error (optional)",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "requestHardware",
|
||||
details: { error: proxmoxErr.message },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return mainResponse;
|
||||
} catch (err: any) {
|
||||
err.service = this.SERVICE_NAME;
|
||||
err.method = "requestHardware";
|
||||
@@ -432,6 +538,22 @@ class NetworkService implements INetworkService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the Proxmox metrics URL from the main metrics URL
|
||||
* e.g., http://localhost:59232/api/v1/metrics -> http://localhost:59232/api/v1/metrics/proxmox
|
||||
*/
|
||||
private buildProxmoxUrl(baseUrl: string | undefined): string | null {
|
||||
if (!baseUrl) return null;
|
||||
try {
|
||||
const url = new URL(baseUrl);
|
||||
// Append /proxmox to the path
|
||||
url.pathname = url.pathname.replace(/\/?$/, "/proxmox");
|
||||
return url.toString();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async requestDocker(monitor: Monitor): Promise<MonitorStatusResponse> {
|
||||
try {
|
||||
if (!monitor.url) {
|
||||
|
||||
@@ -64,6 +64,28 @@ export interface CheckNetworkInterfaceInfo {
|
||||
fifo_out: number;
|
||||
}
|
||||
|
||||
export interface CheckContainerInfo {
|
||||
vmid: string;
|
||||
name: string;
|
||||
node: string;
|
||||
status: string;
|
||||
type: string;
|
||||
uptime: number;
|
||||
cpu_cores: number;
|
||||
cpu_usage: number;
|
||||
memory_used: number;
|
||||
memory_total: number;
|
||||
memory_usage: number;
|
||||
swap_used: number;
|
||||
swap_total: number;
|
||||
disk_used: number;
|
||||
disk_total: number;
|
||||
disk_read: number;
|
||||
disk_write: number;
|
||||
network_in: number;
|
||||
network_out: number;
|
||||
}
|
||||
|
||||
export interface CheckAudits {
|
||||
cls?: ILighthouseAudit;
|
||||
si?: ILighthouseAudit;
|
||||
@@ -99,6 +121,7 @@ export interface Check {
|
||||
errors?: CheckErrorInfo[];
|
||||
capture?: CheckCaptureInfo;
|
||||
net?: CheckNetworkInterfaceInfo[];
|
||||
containers?: CheckContainerInfo[];
|
||||
accessibility?: number;
|
||||
bestPractices?: number;
|
||||
seo?: number;
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface Monitor {
|
||||
statusWindowThreshold: number;
|
||||
type: MonitorType;
|
||||
ignoreTlsErrors: boolean;
|
||||
enableProxmoxContainers: boolean;
|
||||
jsonPath?: string;
|
||||
expectedValue?: string;
|
||||
matchMethod?: MonitorMatchMethod;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
CheckCaptureInfo,
|
||||
CheckContainerInfo,
|
||||
CheckCpuInfo,
|
||||
CheckDiskInfo,
|
||||
CheckErrorInfo,
|
||||
@@ -75,6 +76,7 @@ export interface HardwareStatusMetrics {
|
||||
disk?: CheckDiskInfo[];
|
||||
host?: CheckHostInfo;
|
||||
net?: CheckNetworkInterfaceInfo[];
|
||||
containers?: CheckContainerInfo[];
|
||||
}
|
||||
|
||||
export interface HardwareStatusPayload {
|
||||
|
||||
@@ -158,6 +158,7 @@ const createMonitorBodyValidation = joi.object({
|
||||
statusWindowThreshold: joi.number().min(1).max(100).default(60),
|
||||
url: joi.string().required(),
|
||||
ignoreTlsErrors: joi.boolean().default(false),
|
||||
enableProxmoxContainers: joi.boolean().default(false),
|
||||
port: joi.number(),
|
||||
isActive: joi.boolean(),
|
||||
interval: joi.number(),
|
||||
@@ -193,6 +194,7 @@ const editMonitorBodyValidation = joi.object({
|
||||
notifications: joi.array().items(joi.string()),
|
||||
secret: joi.string(),
|
||||
ignoreTlsErrors: joi.boolean(),
|
||||
enableProxmoxContainers: joi.boolean(),
|
||||
jsonPath: joi.string().allow(""),
|
||||
expectedValue: joi.string().allow(""),
|
||||
matchMethod: joi.string().allow(null, ""),
|
||||
|
||||
Reference in new issue
Block a user