From f8f3c91a8722e0cb5cc78f21ad163e8165ac5979 Mon Sep 17 00:00:00 2001
From: Emiliano Ciavatta
Date: Fri, 25 Dec 2020 14:48:11 +0100
Subject: Add stats page, add /api/statistics/totals
---
frontend/src/components/panels/ConfigPane.js | 39 ----
frontend/src/components/panels/StatsPane.js | 274 ++++++++++++++++++++++++++
frontend/src/components/panels/StatsPane.scss | 21 ++
3 files changed, 295 insertions(+), 39 deletions(-)
delete mode 100644 frontend/src/components/panels/ConfigPane.js
create mode 100644 frontend/src/components/panels/StatsPane.js
create mode 100644 frontend/src/components/panels/StatsPane.scss
(limited to 'frontend/src/components/panels')
diff --git a/frontend/src/components/panels/ConfigPane.js b/frontend/src/components/panels/ConfigPane.js
deleted file mode 100644
index 9710abd..0000000
--- a/frontend/src/components/panels/ConfigPane.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * This file is part of caronte (https://github.com/eciavatta/caronte).
- * Copyright (c) 2020 Emiliano Ciavatta.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-import React, {Component} from "react";
-import "./common.scss";
-
-class ConfigPane extends Component {
-
- state = {};
-
- render() {
- return (
-
- );
- }
-
-}
-
-export default ConfigPane;
diff --git a/frontend/src/components/panels/StatsPane.js b/frontend/src/components/panels/StatsPane.js
new file mode 100644
index 0000000..dd774d0
--- /dev/null
+++ b/frontend/src/components/panels/StatsPane.js
@@ -0,0 +1,274 @@
+/*
+ * This file is part of caronte (https://github.com/eciavatta/caronte).
+ * Copyright (c) 2020 Emiliano Ciavatta.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+import React, {Component} from "react";
+import Table from "react-bootstrap/Table";
+import backend from "../../backend";
+import dispatcher from "../../dispatcher";
+import {formatSize} from "../../utils";
+import ButtonField from "../fields/ButtonField";
+import CopyLinkPopover from "../objects/CopyLinkPopover";
+import LinkPopover from "../objects/LinkPopover";
+import "./common.scss";
+import "./StatsPane.scss";
+
+class StatsPane extends Component {
+
+ state = {
+ rules: []
+ };
+
+ componentDidMount() {
+ this.loadStats();
+ this.loadResourcesStats();
+ this.loadRules();
+ dispatcher.register("notifications", this.handleNotifications);
+ document.title = "caronte:~/stats$";
+ this.intervalToken = setInterval(() => this.loadResourcesStats(), 3000);
+ }
+
+ componentWillUnmount() {
+ dispatcher.unregister(this.handleNotifications);
+ clearInterval(this.intervalToken);
+ }
+
+ handleNotifications = (payload) => {
+ if (payload.event.startsWith("pcap")) {
+ this.loadStats();
+ } else if (payload.event.startsWith("rules")) {
+ this.loadRules();
+ }
+ };
+
+ loadStats = () => {
+ backend.get("/api/statistics/totals")
+ .then((res) => this.setState({stats: res.json, statsStatusCode: res.status}))
+ .catch((res) => this.setState({
+ stats: res.json, statsStatusCode: res.status,
+ statsResponse: JSON.stringify(res.json)
+ }));
+ };
+
+ loadResourcesStats = () => {
+ backend.get("/api/resources/system")
+ .then((res) => this.setState({resourcesStats: res.json, resourcesStatsStatusCode: res.status}))
+ .catch((res) => this.setState({
+ resourcesStats: res.json, resourcesStatsStatusCode: res.status,
+ resourcesStatsResponse: JSON.stringify(res.json)
+ }));
+ };
+
+ loadRules = () => {
+ backend.get("/api/rules").then((res) => this.setState({rules: res.json}));
+ };
+
+ render() {
+ const s = this.state.stats;
+ const rs = this.state.resourcesStats;
+
+ const ports = s ? Object.keys(s["connections_per_service"]) : [];
+ let connections = 0, clientBytes = 0, serverBytes = 0, totalBytes = 0, duration = 0;
+ let servicesStats = ports.map((port) => {
+ connections += s["connections_per_service"][port];
+ clientBytes += s["client_bytes_per_service"][port];
+ serverBytes += s["server_bytes_per_service"][port];
+ totalBytes += s["total_bytes_per_service"][port];
+ duration += s["duration_per_service"][port];
+
+ return
+ {port} |
+ {formatSize(s["connections_per_service"][port])} |
+ {formatSize(s["client_bytes_per_service"][port])}B |
+ {formatSize(s["server_bytes_per_service"][port])}B |
+ {formatSize(s["total_bytes_per_service"][port])}B |
+ {formatSize(s["duration_per_service"][port] / 1000)}s |
+
;
+ });
+ servicesStats.push(
+ totals |
+ {formatSize(connections)} |
+ {formatSize(clientBytes)}B |
+ {formatSize(serverBytes)}B |
+ {formatSize(totalBytes)}B |
+ {formatSize(duration / 1000)}s |
+
);
+
+ const rulesStats = this.state.rules.map((r) =>
+
+ |
+ {r["name"]} |
+ |
+ {formatSize(s && s["matched_rules"] && s["matched_rules"][r.id] ? s["matched_rules"][r.id] : 0)} |
+
+ );
+
+ const cpuStats = (rs ? rs["cpu_times"] : []).map((cpu, index) =>
+
+ {cpu["cpu"]} |
+ {cpu["user"]} |
+ {cpu["system"]} |
+ {cpu["idle"]} |
+ {cpu["nice"]} |
+ {cpu["iowait"]} |
+ {rs["cpu_percents"][index].toFixed(2)} % |
+
+ );
+
+ return (
+
+
+
+ GET /api/statistics/totals
+
+
+
+
+
+
+
+
+ service |
+ connections |
+ client_bytes |
+ server_bytes |
+ total_bytes |
+ duration |
+
+
+
+ {servicesStats}
+
+
+
+
+
+
+
+
+ rule_id |
+ rule_name |
+ rule_color |
+ occurrences |
+
+
+
+ {rulesStats}
+
+
+
+
+
+
+
+
+ GET /api/resources/system
+
+
+
+
+
+
+
+
+ type |
+ total |
+ used |
+ free |
+ shared |
+ buff/cache |
+ available |
+
+
+
+
+ mem |
+ {rs && formatSize(rs["virtual_memory"]["total"])} |
+ {rs && formatSize(rs["virtual_memory"]["used"])} |
+ {rs && formatSize(rs["virtual_memory"]["free"])} |
+ {rs && formatSize(rs["virtual_memory"]["shared"])} |
+ {rs && formatSize(rs["virtual_memory"]["cached"])} |
+ {rs && formatSize(rs["virtual_memory"]["available"])} |
+
+
+ swap |
+ {rs && formatSize(rs["virtual_memory"]["swaptotal"])} |
+ {rs && formatSize(rs["virtual_memory"]["swaptotal"])} |
+ {rs && formatSize(rs["virtual_memory"]["swapfree"])} |
+ - |
+ - |
+ - |
+
+
+
+
+
+
+
+
+
+ cpu |
+ user |
+ system |
+ idle |
+ nice |
+ iowait |
+ used_percent |
+
+
+
+ {cpuStats}
+
+
+
+
+
+
+
+
+ disk_path |
+ fs_type |
+ total |
+ free |
+ used |
+ used_percent |
+
+
+
+
+ {rs && rs["disk_usage"]["path"]} |
+ {rs && rs["disk_usage"]["fstype"]} |
+ {rs && formatSize(rs["disk_usage"]["total"])} |
+ {rs && formatSize(rs["disk_usage"]["free"])} |
+ {rs && formatSize(rs["disk_usage"]["used"])} |
+ {rs && rs["disk_usage"]["usedPercent"].toFixed(2)} % |
+
+
+
+
+
+
+
+ );
+ }
+
+}
+
+export default StatsPane;
diff --git a/frontend/src/components/panels/StatsPane.scss b/frontend/src/components/panels/StatsPane.scss
new file mode 100644
index 0000000..24a0a33
--- /dev/null
+++ b/frontend/src/components/panels/StatsPane.scss
@@ -0,0 +1,21 @@
+@import "../../colors.scss";
+
+.stats-pane {
+ display: flex;
+ flex-direction: column;
+
+ .stats-list {
+ position: relative;
+ flex: 1;
+ height: 50%;
+
+ .section-content {
+ overflow-y: scroll;
+ height: calc(100% - 10px);
+ }
+
+ .section-table {
+ margin-bottom: 10px;
+ }
+ }
+}
--
cgit v1.2.3-70-g09d2