mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
* The JavaScript RingBuffer structure updated correctly. * Replaced the funnel graph with text - easier to read. * Discovered that the current "parking_lot" could become unstable under very heavy load, and only with "fat" LTO. Since it's no longer recommended (recent change), removed it. * Replaced the "lazy_static" macro suite with the newly recommended "once_cell" system. Less code. * Full source format. * Update some dependency versions.
336 lines
13 KiB
HTML
336 lines
13 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link href="/vendor/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="/vendor/solid.min.css">
|
|
<link rel="stylesheet" href="/lqos.css">
|
|
<title>LibreQoS - Local Node Manager</title>
|
|
<script src="/lqos.js"></script>
|
|
<script src="/vendor/plotly-2.16.1.min.js"></script>
|
|
<script src="/vendor/jquery.min.js"></script>
|
|
<script defer src="/vendor/bootstrap.bundle.min.js"></script>
|
|
</head>
|
|
|
|
<body class="bg-secondary">
|
|
<!-- Navigation -->
|
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
|
<div class="container-fluid">
|
|
<a class="navbar-brand" href="/"><img src="/vendor/tinylogo.svg" alt="LibreQoS SVG Logo" width="25"
|
|
height="25" /> LibreQoS</a>
|
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
|
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
|
|
aria-label="Toggle navigation">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
|
<li class="nav-item">
|
|
<a class="nav-link active" aria-current="page" href="/"><i class="fa fa-home"></i> Dashboard</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="/tree?parent=0"><i class="fa fa-globe"></i> Tree</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="/shaped"><i class="fa fa-users"></i> Shaped Devices <span
|
|
id="shapedCount" class="badge badge-pill badge-success green-badge">?</span></a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="/unknown"><i class="fa fa-address-card"></i> Unknown IPs <span
|
|
id="unshapedCount" class="badge badge-warning orange-badge">?</span></a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<ul class="navbar-nav ms-auto">
|
|
<li class="nav-item" id="currentLogin"></li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#" id="startTest"><i class="fa fa-flag-checkered"></i> Run Bandwidth
|
|
Test</a>
|
|
</li>
|
|
<li class="nav-item ms-auto">
|
|
<a class="nav-link" href="/config"><i class="fa fa-gear"></i> Configuration</a>
|
|
</li>
|
|
<li>
|
|
<a class="nav-link btn btn-small" href="#" id="btnReload"><i class="fa fa-refresh"></i> Reload
|
|
LibreQoS</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<div id="container" class="pad4">
|
|
|
|
<!-- Dashboard Row 1 -->
|
|
<div class="row mbot8">
|
|
<!-- THROUGHPUT -->
|
|
<div class="col-sm-4">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h5 class="card-title"><i class="fa fa-bolt"></i> Current Throughput</h5>
|
|
<table class="table">
|
|
<tr>
|
|
<td class="bold">Packets/Second</td>
|
|
<td id="ppsDown"></td>
|
|
<td id="ppsUp"></td>
|
|
</tr>
|
|
<tr>
|
|
<td class="bold">Bits/Second</td>
|
|
<td id="bpsDown"></td>
|
|
<td id="bpsUp"></td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RAM INFO -->
|
|
<div class="col-sm-2">
|
|
<div class="card bg-light d-none d-lg-block">
|
|
<div class="card-body">
|
|
<h5 class="card-title"><i class="fa fa-database"></i> Memory Status</h5>
|
|
<div id="ram" class="graph98"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- CPU INFO -->
|
|
<div class="col-sm-6">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h5 class="card-title"><i class="fa fa-microchip"></i> CPU Status</h5>
|
|
<div id="cpu" class="graph98"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dashboard Row 2 -->
|
|
<div class="row mbot8 row220">
|
|
<!-- 5 minutes of throughput -->
|
|
<div class="col-sm-4">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h5 class="card-title"><i class="fa fa-hourglass"></i> Last 5 Minutes</h5>
|
|
<div id="tpGraph" class="graph98 graph150"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RTT Histogram -->
|
|
<div class="col-sm-4">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h5 class="card-title"><i class="fa fa-bar-chart"></i> TCP Round-Trip Time Histogram</h5>
|
|
<div id="rttHistogram" class="graph98 graph150"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Site Funnel -->
|
|
<div class="col-sm-4">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h5 class="card-title"><i class="fa fa-globe"></i> Site Funnel</h5>
|
|
<div id="siteFunnel" class="graph98 graph150"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dashboard Row 3 -->
|
|
<div class="row">
|
|
<!-- Top 10 downloaders -->
|
|
<div class="col-sm-6">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h5 class="card-title"><i class='fa fa-arrow-down'></i> Top 10 Downloaders</h5>
|
|
<div id="top10dl"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Worst 10 RTT -->
|
|
<div class="col-sm-6">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h5 class="card-title"><i class='fa fa-exclamation'></i> Worst 10 RTT</h5>
|
|
<div id="worstRtt"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<footer>© 2022-2023, LibreQoE LLC</footer>
|
|
|
|
<script>
|
|
var throughput = new MultiRingBuffer(300);
|
|
|
|
function updateCurrentThroughput() {
|
|
$.get("/api/current_throughput", (tp) => {
|
|
$("#ppsDown").text(scaleNumber(tp.packets_per_second[0]));
|
|
$("#ppsUp").text(scaleNumber(tp.packets_per_second[1]));
|
|
$("#bpsDown").text(scaleNumber(tp.bits_per_second[0]));
|
|
$("#bpsUp").text(scaleNumber(tp.bits_per_second[1]));
|
|
|
|
throughput.push("pps", tp.packets_per_second[0], tp.packets_per_second[1]);
|
|
throughput.push("total", tp.bits_per_second[0], tp.bits_per_second[1]);
|
|
throughput.push("shaped", tp.shaped_bits_per_second[0], tp.shaped_bits_per_second[1]);
|
|
throughput.plotTotalThroughput("tpGraph");
|
|
});
|
|
}
|
|
|
|
var funnelData = new MultiRingBuffer(300);
|
|
|
|
function updateSiteFunnel() {
|
|
$.get("/api/network_tree_summary/", (data) => {
|
|
let table = "<table class='table' style='font-size: 8pt;'>";
|
|
for (let i = 0; i < data.length; ++i) {
|
|
let name = data[i][1].name;
|
|
if (name.length > 20) {
|
|
name = name.substring(0, 20) + "...";
|
|
}
|
|
table += "<tr>";
|
|
table += "<td class='redact'>" + redactText(name) + "</td>";
|
|
table += "<td>" + scaleNumber(data[i][1].current_throughput[0] * 8) + "</td>";
|
|
table += "<td>" + scaleNumber(data[i][1].current_throughput[1] * 8) + "</td>";
|
|
table += "</tr>";
|
|
}
|
|
table += "</table>";
|
|
$("#siteFunnel").html(table);
|
|
});
|
|
}
|
|
|
|
function updateCpu() {
|
|
$.get("/api/cpu", (cpu) => {
|
|
let graph = document.getElementById("cpu");
|
|
let x = [];
|
|
let y = [];
|
|
let colors = [];
|
|
for (i = 0; i < cpu.length; i++) {
|
|
x.push(i);
|
|
y.push(cpu[i]);
|
|
colors.push(cpu[i]);
|
|
}
|
|
colors.push(100); // 1 extra colors entry to force color scaling
|
|
let data = [{ x: x, y: y, type: 'bar', marker: { color: colors, colorscale: 'Jet' } }];
|
|
Plotly.newPlot(graph, data, {
|
|
margin: { l: 0, r: 0, b: 15, t: 0 },
|
|
yaxis: { automargin: true, autorange: false, range: [0.0, 100.0] },
|
|
},
|
|
{ responsive: true });
|
|
});
|
|
}
|
|
|
|
function updateRam() {
|
|
$.get("/api/ram", (ram) => {
|
|
let graph = document.getElementById("ram");
|
|
let data = [{
|
|
values: [ram[0], ram[1] - ram[0]],
|
|
labels: ['Used', 'Available'],
|
|
type: 'pie'
|
|
}];
|
|
Plotly.newPlot(graph, data, { margin: { l: 0, r: 0, b: 0, t: 12 }, showlegend: false }, { responsive: true });
|
|
});
|
|
}
|
|
|
|
function updateNTable(target, tt) {
|
|
let html = "<table class='table'>";
|
|
html += "<thead><th>IP Address</th><th>DL ⬇️</th><th>UL ⬆️</th><th>RTT (ms)</th><th>Shaped</th></thead>";
|
|
for (let i = 0; i < tt.length; i++) {
|
|
let color = color_ramp(tt[i].median_tcp_rtt);
|
|
html += "<tr style='background-color: " + color + "'>";
|
|
if (tt[i].circuit_id != "") {
|
|
html += "<td><a class='redact' href='/circuit_queue?id=" + encodeURI(tt[i].circuit_id) + "'>" + redactText(tt[i].ip_address) + "</td>";
|
|
} else {
|
|
html += "<td><span class='redact'>" + redactText(tt[i].ip_address) + "</span></td>";
|
|
}
|
|
html += "<td>" + scaleNumber(tt[i].bits_per_second[0]) + "</td>";
|
|
html += "<td>" + scaleNumber(tt[i].bits_per_second[1]) + "</td>";
|
|
html += "<td>" + tt[i].median_tcp_rtt.toFixed(2) + "</td>";
|
|
if (tt[i].tc_handle != 0) {
|
|
html += "<td><i class='fa fa-check-circle'></i> (" + tt[i].plan[0] + "/" + tt[i].plan[1] + ")</td>";
|
|
} else {
|
|
//html += "<td><a class='btn btn-small btn-success' href='/shaped-add?ip=" + tt[i].ip_address + "'>Add Shaper</a></td>";
|
|
html += "<td>Not Shaped</td>"
|
|
}
|
|
html += "</tr>";
|
|
}
|
|
html += "</table>";
|
|
$(target).html(html);
|
|
}
|
|
|
|
function updateTop10() {
|
|
$.get("/api/top_10_downloaders", (tt) => {
|
|
updateNTable('#top10dl', tt);
|
|
});
|
|
}
|
|
|
|
function updateWorst10() {
|
|
$.get("/api/worst_10_rtt", (tt) => {
|
|
updateNTable('#worstRtt', tt);
|
|
});
|
|
}
|
|
|
|
var rttGraph = new RttHistogram();
|
|
|
|
function updateHistogram() {
|
|
$.get("/api/rtt_histogram", (rtt) => {
|
|
rttGraph.clear();
|
|
for (let i = 0; i < rtt.length; i++) {
|
|
rttGraph.pushBand(i, rtt[i]);
|
|
}
|
|
rttGraph.plot("rttHistogram");
|
|
});
|
|
}
|
|
|
|
var tickCount = 0;
|
|
|
|
function OneSecondCadence() {
|
|
updateCurrentThroughput();
|
|
updateSiteFunnel();
|
|
|
|
if (tickCount % 5 == 0) {
|
|
updateHistogram();
|
|
updateWorst10();
|
|
updateTop10();
|
|
}
|
|
|
|
if (tickCount % 10 == 0) {
|
|
updateCpu();
|
|
updateRam();
|
|
}
|
|
|
|
tickCount++;
|
|
setTimeout(OneSecondCadence, 1000);
|
|
}
|
|
|
|
function start() {
|
|
if (isRedacted()) {
|
|
//console.log("Redacting");
|
|
//css_getclass(".redact").style.filter = "blur(4px)";
|
|
css_getclass(".redact").style.fontFamily = "klingon";
|
|
}
|
|
|
|
colorReloadButton();
|
|
updateCurrentThroughput();
|
|
updateCpu();
|
|
updateRam();
|
|
updateTop10();
|
|
updateWorst10();
|
|
updateHistogram();
|
|
updateHostCounts();
|
|
updateSiteFunnel();
|
|
OneSecondCadence();
|
|
}
|
|
|
|
$(document).ready(start);
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html> |