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/Header.js | 4 +- frontend/src/components/pages/MainPage.js | 4 +- frontend/src/components/panels/ConfigPane.js | 39 ---- frontend/src/components/panels/StatsPane.js | 274 ++++++++++++++++++++++++++ frontend/src/components/panels/StatsPane.scss | 21 ++ frontend/src/utils.js | 4 +- 6 files changed, 302 insertions(+), 44 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') diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index 4db05a5..e3fafc4 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -85,8 +85,8 @@ class Header extends Component { - - + + } diff --git a/frontend/src/components/pages/MainPage.js b/frontend/src/components/pages/MainPage.js index 6058edc..f005f44 100644 --- a/frontend/src/components/pages/MainPage.js +++ b/frontend/src/components/pages/MainPage.js @@ -21,13 +21,13 @@ import "react-reflex/styles.css" import {Route, Switch} from "react-router-dom"; import Filters from "../dialogs/Filters"; import Header from "../Header"; -import ConfigPane from "../panels/ConfigPane"; import Connections from "../panels/ConnectionsPane"; import MainPane from "../panels/MainPane"; import PcapsPane from "../panels/PcapsPane"; import RulesPane from "../panels/RulesPane"; import SearchPane from "../panels/SearchPane"; import ServicesPane from "../panels/ServicesPane"; +import StatsPane from "../panels/StatsPane"; import StreamsPane from "../panels/StreamsPane"; import Timeline from "../Timeline"; import "./MainPage.scss"; @@ -76,7 +76,7 @@ class MainPage extends Component { }/> }/> }/> - }/> + }/> }/> }/> 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 ( -
-
-
- Not implemented -
-
-
- ); - } - -} - -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 + +
+ +
+
+ + + + + + + + + + + + + {servicesStats} + +
serviceconnectionsclient_bytesserver_bytestotal_bytesduration
+
+ +
+ + + + + + + + + + + {rulesStats} + +
rule_idrule_namerule_coloroccurrences
+
+
+
+ +
+
+ GET /api/resources/system + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
typetotalusedfreesharedbuff/cacheavailable
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"])}---
+
+ +
+ + + + + + + + + + + + + + {cpuStats} + +
cpuusersystemidleniceiowaitused_percent
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
disk_pathfs_typetotalfreeusedused_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; + } + } +} diff --git a/frontend/src/utils.js b/frontend/src/utils.js index 445e576..4849a92 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -120,8 +120,10 @@ export function formatSize(size) { return `${size}`; } else if (size < 1000000) { return `${(size / 1000).toFixed(1)}K`; - } else { + } else if (size < 1000000000) { return `${(size / 1000000).toFixed(1)}M`; + } else { + return `${(size / 1000000000).toFixed(1)}Gi`; } } -- cgit v1.2.3-70-g09d2