VirtWeb/virtweb_frontend/src/routes/SysInfoRoute.tsx

344 lines
9.2 KiB
TypeScript
Raw Normal View History

2023-09-08 12:00:39 +00:00
import {
mdiHarddisk,
mdiInformation,
mdiMemory,
mdiNetwork,
mdiPackageVariantClosed,
} from "@mdi/js";
2023-09-08 10:23:15 +00:00
import Icon from "@mdi/react";
import {
Box,
Grid,
2023-09-08 12:00:39 +00:00
LinearProgress,
2023-09-08 10:23:15 +00:00
Table,
TableBody,
TableCell,
2023-09-08 12:00:39 +00:00
TableHead,
2023-09-08 10:23:15 +00:00
TableRow,
Typography,
} from "@mui/material";
import { PieChart } from "@mui/x-charts";
2023-09-08 07:45:41 +00:00
import React from "react";
2023-09-08 12:00:39 +00:00
import {
DiskInfo,
NetworkInfo,
ServerApi,
ServerSystemInfo,
} from "../api/ServerApi";
2023-09-08 07:45:41 +00:00
import { AsyncWidget } from "../widgets/AsyncWidget";
2023-09-08 10:23:15 +00:00
import { VirtWebPaper } from "../widgets/VirtWebPaper";
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
import humanizeDuration from "humanize-duration";
2023-09-08 12:00:39 +00:00
import { filesize } from "filesize";
2023-09-08 07:45:41 +00:00
export function SysInfoRoute(): React.ReactElement {
const [info, setInfo] = React.useState<ServerSystemInfo>();
const load = async () => {
setInfo(await ServerApi.SystemInfo());
};
return (
<AsyncWidget
load={load}
loadKey={1}
build={() => <SysInfoRouteInner info={info!} />}
errMsg="Failed to load system info"
/>
);
}
export function SysInfoRouteInner(p: {
info: ServerSystemInfo;
}): React.ReactElement {
2023-09-08 10:23:15 +00:00
const sumDiskUsage = p.info.system.disks.reduce(
(prev, disk) => {
return {
used: prev.used + disk.total_space - disk.available_space,
free: prev.free + disk.available_space,
};
},
{ used: 0, free: 0 }
);
return (
<VirtWebRouteContainer label="Sysinfo">
<Grid container spacing={2}>
{/* Memory */}
<Grid xs={4}>
<Box flexGrow={1}>
<Typography style={{ textAlign: "center" }}>Memory</Typography>
<PieChart
series={[
{
data: [
{
id: 3,
value: p.info.system.free_memory,
label: "Free",
},
{
id: 2,
value: p.info.system.available_memory,
label: "Available",
},
{
id: 1,
value: p.info.system.used_memory,
label: "Used",
},
],
},
]}
width={400}
height={200}
/>
</Box>
</Grid>
{/* Disk usage */}
<Grid xs={4}>
<Box flexGrow={1}>
<Typography style={{ textAlign: "center" }}>Disk usage</Typography>
<PieChart
series={[
{
data: [
{
id: 1,
value: sumDiskUsage.free,
label: "Free",
},
{
id: 2,
value: sumDiskUsage.used,
label: "Used",
},
],
},
]}
width={400}
height={200}
/>
</Box>
</Grid>
{/* CPU usage */}
<Grid xs={4}>
<Box flexGrow={1}>
<Typography style={{ textAlign: "center" }}>CPU usage</Typography>
<PieChart
series={[
{
data: [
{
id: 1,
value: 100 - p.info.system.global_cpu_info.cpu_usage,
label: "Free",
},
{
id: 2,
value: p.info.system.global_cpu_info.cpu_usage,
label: "Used",
},
],
},
]}
width={400}
height={200}
/>
</Box>
</Grid>
</Grid>
<SysInfoDetailsTable
label="General"
icon={<Icon size={"1rem"} path={mdiInformation} />}
entries={[
{
label: "Load",
value: `${p.info.system.load_average.one} ${p.info.system.load_average.five} ${p.info.system.load_average.fifteen}`,
},
{
label: "Uptime",
value: humanizeDuration(p.info.system.uptime * 1000),
},
{
label: "Bootime",
value: new Date(p.info.system.boot_time * 1000).toString(),
},
{
label: "Hypvervisor type",
value: p.info.hypervisor.type,
},
]}
/>
<SysInfoDetailsTable
label="CPU info"
icon={<Icon size={"1rem"} path={mdiMemory} />}
entries={[
{ label: "Brand", value: p.info.system.global_cpu_info.brand },
{
label: "Vendor ID",
value: p.info.system.global_cpu_info.vendor_id,
},
{
label: "CPU usage",
value: p.info.system.global_cpu_info.cpu_usage,
},
{
label: "Name",
value: p.info.system.global_cpu_info.name,
},
{
label: "CPU model",
value: p.info.hypervisor.node.cpu_model,
},
{
label: "CPU frequency (MHz)",
value: p.info.hypervisor.node.cpu_frequency_mhz,
},
{
label: "Number of socket",
value: p.info.hypervisor.node.number_of_cpu_socket_per_node,
},
{
label: "Number of cores per socket",
value: p.info.hypervisor.node.number_of_core_per_sockets,
},
{
label: "Number of threads per core",
value: p.info.hypervisor.node.number_of_threads_per_core,
},
]}
/>
<SysInfoDetailsTable
label="OS info"
icon={<Icon size={"1rem"} path={mdiPackageVariantClosed} />}
entries={[
{ label: "Name", value: p.info.system.name },
{ label: "Host name", value: p.info.system.host_name },
{ label: "Long OS version", value: p.info.system.long_os_version },
{ label: "Kernel version", value: p.info.system.kernel_version },
]}
/>
2023-09-08 12:00:39 +00:00
<DiskDetailsTable disks={p.info.system.disks} />
<NetworksDetailsTable networks={p.info.system.networks} />
2023-09-08 10:23:15 +00:00
</VirtWebRouteContainer>
);
}
function SysInfoDetailsTable(p: {
label: string;
icon: React.ReactElement;
entries: Array<{ label: string; value: string | number }>;
}): React.ReactElement {
return (
<VirtWebPaper
label={
<>
{p.icon} {p.label}
</>
}
>
<Table>
<TableBody>
{p.entries.map((e, c) => (
<TableRow hover key={c}>
<TableCell style={{ padding: 5, fontWeight: "bold" }}>
{e.label}
</TableCell>
<TableCell style={{ padding: 5 }}>{e.value}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</VirtWebPaper>
);
2023-09-08 07:45:41 +00:00
}
2023-09-08 12:00:39 +00:00
function DiskDetailsTable(p: { disks: DiskInfo[] }): React.ReactElement {
return (
<VirtWebPaper
label={
<>
<Icon size={1} path={mdiHarddisk} /> Storage
</>
}
>
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Kind</TableCell>
<TableCell>Mount point</TableCell>
<TableCell>Total space</TableCell>
<TableCell>Free space</TableCell>
<TableCell></TableCell>
<TableCell>Removable</TableCell>
</TableRow>
</TableHead>
<TableBody>
{p.disks.map((e, c) => (
<TableRow hover key={c}>
<TableCell>{e.name}</TableCell>
<TableCell>{e.DiskKind}</TableCell>
<TableCell>{e.mount_point}</TableCell>
<TableCell>{filesize(e.total_space)}</TableCell>
<TableCell>{filesize(e.available_space)}</TableCell>
<TableCell>
<LinearProgress
variant="determinate"
style={{ minWidth: "100px", width: "100%" }}
value={
100 * ((e.total_space - e.available_space) / e.total_space)
}
/>
</TableCell>
<TableCell>{e.is_removable ? "Yes" : "No"}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</VirtWebPaper>
);
}
function NetworksDetailsTable(p: {
networks: NetworkInfo[];
}): React.ReactElement {
return (
<VirtWebPaper
label={
<>
<Icon size={1} path={mdiNetwork} /> Networks
</>
}
>
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Total received</TableCell>
<TableCell>Total transmitted</TableCell>
</TableRow>
</TableHead>
<TableBody>
{p.networks.map((e, c) => (
<TableRow hover key={c}>
<TableCell>{e[0]}</TableCell>
<TableCell>{filesize(e[1].total_received)}</TableCell>
<TableCell>{filesize(e[1].total_transmitted)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</VirtWebPaper>
);
}