mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
Lots of polish. Funnel chart on the front page, rename menu item to tree, move login to the right section, breadcrumbs in the tree.
This commit is contained in:
parent
fc5b9ad3d4
commit
9fa1318350
@ -117,6 +117,13 @@ pub enum BusRequest {
|
||||
parent: usize
|
||||
},
|
||||
|
||||
/// Retrieves the top N queues from the root level, and summarizes
|
||||
/// the others as "other"
|
||||
TopMapQueues(usize),
|
||||
|
||||
/// Retrieve node names from network.json
|
||||
GetNodeNamesFromIds(Vec<usize>),
|
||||
|
||||
/// If running on Equinix (the `equinix_test` feature is enabled),
|
||||
/// display a "run bandwidht test" link.
|
||||
#[cfg(feature = "equinix_tests")]
|
||||
|
@ -71,4 +71,7 @@ pub enum BusResponse {
|
||||
|
||||
/// Results from network map queries
|
||||
NetworkMap(Vec<(usize, lqos_config::NetworkJsonNode)>),
|
||||
|
||||
/// Named nodes from network.json
|
||||
NodeNames(Vec<(usize, String)>),
|
||||
}
|
||||
|
@ -38,7 +38,9 @@ pub struct NetworkJsonNode {
|
||||
/// for easy use in funnel calculations.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct NetworkJson {
|
||||
nodes: Vec<NetworkJsonNode>,
|
||||
/// Nodes that make up the tree, flattened and referenced by index number.
|
||||
/// TODO: We should add a primary key to nodes in network.json.
|
||||
pub nodes: Vec<NetworkJsonNode>,
|
||||
}
|
||||
|
||||
impl Default for NetworkJson {
|
||||
@ -193,11 +195,12 @@ fn recurse_node(
|
||||
immediate_parent: usize,
|
||||
) {
|
||||
info!("Mapping {name} from network.json");
|
||||
let my_id = if name != "children" {
|
||||
/*let my_id = if name != "children" {
|
||||
nodes.len()
|
||||
} else {
|
||||
nodes.len()-1
|
||||
};
|
||||
};*/
|
||||
let my_id = nodes.len();
|
||||
let mut parents = parents.to_vec();
|
||||
parents.push(my_id);
|
||||
let node = NetworkJsonNode {
|
||||
@ -212,9 +215,9 @@ fn recurse_node(
|
||||
rtts: Vec::new(),
|
||||
};
|
||||
|
||||
if node.name != "children" {
|
||||
//if node.name != "children" {
|
||||
nodes.push(node);
|
||||
}
|
||||
//}
|
||||
|
||||
// Recurse children
|
||||
for (key, value) in json.iter() {
|
||||
|
@ -79,6 +79,8 @@ fn rocket() -> _ {
|
||||
auth_guard::username,
|
||||
network_tree::tree_entry,
|
||||
network_tree::tree_clients,
|
||||
network_tree::network_tree_summary,
|
||||
network_tree::node_names,
|
||||
// Supporting files
|
||||
static_pages::bootsrap_css,
|
||||
static_pages::plotly_js,
|
||||
|
@ -27,6 +27,17 @@ pub async fn tree_entry(
|
||||
NoCache::new(Json(result))
|
||||
}
|
||||
|
||||
#[get("/api/network_tree_summary")]
|
||||
pub async fn network_tree_summary() -> NoCache<Json<Vec<(usize, NetworkJsonNode)>>> {
|
||||
let responses =
|
||||
bus_request(vec![BusRequest::TopMapQueues(4)]).await.unwrap();
|
||||
let result = match &responses[0] {
|
||||
BusResponse::NetworkMap(nodes) => nodes.to_owned(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
NoCache::new(Json(result))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct CircuitThroughput {
|
||||
@ -69,3 +80,17 @@ pub async fn tree_clients(
|
||||
}
|
||||
NoCache::new(Json(result))
|
||||
}
|
||||
|
||||
#[post("/api/node_names", data= "<nodes>")]
|
||||
pub async fn node_names(nodes: Json<Vec<usize>>) -> NoCache<Json<Vec<(usize, String)>>> {
|
||||
let mut result = Vec::new();
|
||||
for msg in
|
||||
bus_request(vec![BusRequest::GetNodeNamesFromIds(nodes.0)]).await.unwrap().iter()
|
||||
{
|
||||
if let BusResponse::NodeNames(map) = msg {
|
||||
result.extend_from_slice(map);
|
||||
}
|
||||
}
|
||||
|
||||
NoCache::new(Json(result))
|
||||
}
|
@ -25,10 +25,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/"><i class="fa fa-home"></i> Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<!--<li class="nav-item">
|
||||
<a class="nav-link" href="#"><i class="fa fa-globe"></i> Network Layout</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 active" aria-current="page" href="/shaped"><i class="fa fa-users"></i> Shaped Devices <span id="shapedCount" class="badge badge-pill badge-success green-badge">?</span></a>
|
||||
</li>
|
||||
@ -39,6 +38,7 @@
|
||||
</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>
|
||||
|
@ -25,10 +25,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/"><i class="fa fa-home"></i> Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<!--<li class="nav-item">
|
||||
<a class="nav-link" href="#"><i class="fa fa-globe"></i> Network Layout</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" aria-current="page" href="/shaped"><i class="fa fa-users"></i> Shaped Devices <span id="shapedCount" class="badge badge-pill badge-success green-badge">?</span></a>
|
||||
</li>
|
||||
@ -39,6 +38,7 @@
|
||||
</div>
|
||||
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<li class="nav-item ms-auto">
|
||||
<a class="nav-link active" href="/config"><i class="fa fa-gear"></i> Configuration</a>
|
||||
</li>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
@ -12,12 +13,16 @@
|
||||
<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">
|
||||
<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">
|
||||
@ -25,28 +30,32 @@
|
||||
<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" id="currentLogin"></li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/tree?parent=0"><i class="fa fa-globe"></i> Network Layout</a>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<a class="nav-link btn btn-small" href="#" id="btnReload"><i class="fa fa-refresh"></i> Reload
|
||||
LibreQoS</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -54,106 +63,106 @@
|
||||
|
||||
<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>
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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>
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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>
|
||||
<!-- 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>
|
||||
</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>
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- Idle/Activity Quantiles -->
|
||||
<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> Utilization Quantiles</h5>
|
||||
<div id="capacityHistogram" 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>
|
||||
@ -177,32 +186,16 @@
|
||||
});
|
||||
}
|
||||
|
||||
function updateThroughputQuantile() {
|
||||
$.get("/api/busy_quantile", (tp) => {
|
||||
//console.log(tp);
|
||||
let graph = document.getElementById("capacityHistogram");
|
||||
let x1 = [];
|
||||
let x2 = [];
|
||||
let y1 = [];
|
||||
let y2 = [];
|
||||
for (let i=0; i<10; i++) {
|
||||
x1.push(i*10);
|
||||
x2.push(i*10);
|
||||
if (i > 0) {
|
||||
y1.push(tp[i][0]);
|
||||
y2.push(tp[i][1]);
|
||||
} else {
|
||||
y1.push(0);
|
||||
y2.push(0);
|
||||
}
|
||||
let funnelData = new MultiRingBuffer(300);
|
||||
|
||||
function updateSiteFunnel() {
|
||||
$.get("/api/network_tree_summary/", (data) => {
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
funnelData.push(data[i][1].name, data[i][1].current_throughput[0] * 8, data[i][1].current_throughput[1] * 8);
|
||||
}
|
||||
let data = [
|
||||
{x: x1, y:y1, type: 'bar', name: 'Download'},
|
||||
{x: x1, y:y2, type: 'bar', name: 'Upload'},
|
||||
];
|
||||
Plotly.newPlot(graph, data, { margin: { l:0,r:0,b:0,t:0,pad:4 }, yaxis: { automargin: true, title: '# Samples' }, xaxis: {automargin: true, title: "% utilization"} }, { responsive: true });
|
||||
setTimeout(updateThroughputQuantile, 1000);
|
||||
funnelData.plotStackedBars("siteFunnel", "");
|
||||
});
|
||||
setTimeout(updateSiteFunnel, 1000);
|
||||
}
|
||||
|
||||
function updateCpu() {
|
||||
@ -211,18 +204,18 @@
|
||||
let x = [];
|
||||
let y = [];
|
||||
let colors = [];
|
||||
for (i=0; i<cpu.length; i++) {
|
||||
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 });
|
||||
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 });
|
||||
setTimeout(updateCpu, 2000);
|
||||
});
|
||||
}
|
||||
@ -230,12 +223,12 @@
|
||||
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 });
|
||||
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 });
|
||||
setTimeout(updateRam, 30000);
|
||||
});
|
||||
}
|
||||
@ -243,18 +236,18 @@
|
||||
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++) {
|
||||
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>";
|
||||
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) {
|
||||
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>";
|
||||
@ -285,7 +278,7 @@
|
||||
function updateHistogram() {
|
||||
$.get("/api/rtt_histogram", (rtt) => {
|
||||
rttGraph.clear();
|
||||
for (let i=0; i<rtt.length; i++) {
|
||||
for (let i = 0; i < rtt.length; i++) {
|
||||
rttGraph.push(rtt[i]);
|
||||
}
|
||||
rttGraph.plot("rttHistogram");
|
||||
@ -308,11 +301,12 @@
|
||||
updateWorst10();
|
||||
updateHistogram();
|
||||
updateHostCounts();
|
||||
//updateThroughputQuantile();
|
||||
updateSiteFunnel();
|
||||
}
|
||||
|
||||
$(document).ready(start);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
@ -25,10 +25,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/"><i class="fa fa-home"></i> Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<!--<li class="nav-item">
|
||||
<a class="nav-link" href="#"><i class="fa fa-globe"></i> Network Layout</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 active" aria-current="page" href="/shaped"><i class="fa fa-users"></i> Shaped Devices <span id="shapedCount" class="badge badge-pill badge-success green-badge">?</span></a>
|
||||
</li>
|
||||
@ -39,6 +38,7 @@
|
||||
</div>
|
||||
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<li class="nav-item ms-auto">
|
||||
<a class="nav-link" href="/config"><i class="fa fa-gear"></i> Configuration</a>
|
||||
</li>
|
||||
|
@ -25,10 +25,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/"><i class="fa fa-home"></i> Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<!--<li class="nav-item">
|
||||
<a class="nav-link" href="#"><i class="fa fa-globe"></i> Network Layout</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 active" aria-current="page" href="/shaped"><i class="fa fa-users"></i> Shaped Devices <span id="shapedCount" class="badge badge-pill badge-success green-badge">?</span></a>
|
||||
</li>
|
||||
@ -39,6 +38,7 @@
|
||||
</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>
|
||||
|
@ -32,7 +32,7 @@
|
||||
</li>
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/tree?parent=0"><i class="fa fa-globe"></i> Network Layout</a>
|
||||
<a class="nav-link active" 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
|
||||
@ -85,13 +85,14 @@
|
||||
</div>
|
||||
|
||||
<!-- Info -->
|
||||
<div class="col-sm-3">
|
||||
<div class="col-sm-4">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-globe"></i> <span id="nodeName"
|
||||
style="font-weight: bold;" class='redact'></span></h5>
|
||||
<strong>DL Limit</strong>: <span id="nodeDL"></span><br />
|
||||
<strong>UL Limit</strong>: <span id="nodeUL"></span><br />
|
||||
<strong>UL Limit</strong>: <span id="nodeUL"></span><br />
|
||||
<div id="breadcrumbs"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,14 +100,24 @@
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top: 4px;">
|
||||
<div class="col-sm-12 bg-light center-txt">
|
||||
<div id="clientList"></div>
|
||||
<!-- List of network circuits -->
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-globe"></i> Child Nodes</h5>
|
||||
<div id="treeList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top: 4px;">
|
||||
<div class="col-sm-12 bg-light center-txt">
|
||||
<div id="treeList"></div>
|
||||
<!-- List of client circuits -->
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-users"></i> Attached Clients</h5>
|
||||
<div id="clientList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -122,7 +133,7 @@
|
||||
if (limit == 0) {
|
||||
return "#ddffdd";
|
||||
}
|
||||
let usage = (traffic*8) / (limit * 1000000);
|
||||
let usage = (traffic * 8) / (limit * 1000000);
|
||||
if (usage < 0.25) { return "#ddffdd" }
|
||||
else if (usage < 0.5) { return "#aaffaa" }
|
||||
else if (usage < 0.75) { return "#ffa500" }
|
||||
@ -131,16 +142,18 @@
|
||||
|
||||
function getClients(rootName) {
|
||||
$.get("/api/tree_clients/" + encodeURI(rootName), (data) => {
|
||||
let tbl = "<table class='table'>";
|
||||
tbl += "<thead><th>Circuit</th><th>Limit</th><th>Download</th><th>Upload</th></thead>";
|
||||
|
||||
for (let i=0; i<data.length; ++i) {
|
||||
let tbl = "<table class='table table-striped'>";
|
||||
tbl += "<thead><th>Circuit</th><th>Limit</th><th>⬇️ DL</th><th>⬆️ UL</th></thead>";
|
||||
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
let nodeDL = scaleNumber(data[i].limit[0] * 1000000);
|
||||
let nodeUL = scaleNumber(data[i].limit[1] * 1000000);
|
||||
if (nodeDL == "0") nodeDL = "Unlimited";
|
||||
if (nodeUL == "0") nodeUL = "Unlimited";
|
||||
tbl += "<tr>";
|
||||
tbl += "<td class='redact'><a href='/circuit_queue?id=" + encodeURI(data[i].id) + "'>" + redactText(data[i].name) + "</a></td>";
|
||||
let displayName = data[i].name;
|
||||
if (displayName.length > 30) displayName = displayName.substring(0, 30) + "...";
|
||||
tbl += "<td class='redact'><a href='/circuit_queue?id=" + encodeURI(data[i].id) + "'>" + redactText(displayName) + "</a></td>";
|
||||
tbl += "<td>" + nodeDL + " / " + nodeUL + "</td>";
|
||||
let upbg = bgColor(data[i].traffic[1], data[i].limit[1]);
|
||||
let dnbg = bgColor(data[i].traffic[0], data[0].limit[1]);
|
||||
@ -154,19 +167,51 @@
|
||||
});
|
||||
}
|
||||
|
||||
let filled_root = false;
|
||||
|
||||
function getTree() {
|
||||
$.get("/api/network_tree/" + node, (data) => {
|
||||
rtt_histo.clear();
|
||||
//console.log(data);
|
||||
// Setup "this node"
|
||||
let rootName = data[0][1].name;
|
||||
$("#nodeName").text(redactText(rootName));
|
||||
let nodeDL = scaleNumber(data[0][1].max_throughput[0] * 1000000);
|
||||
let nodeUL = scaleNumber(data[0][1].max_throughput[1] * 1000000);
|
||||
if (nodeDL == "0") nodeDL = "Unlimited";
|
||||
if (nodeUL == "0") nodeUL = "Unlimited";
|
||||
$("#nodeDL").text(nodeDL);
|
||||
$("#nodeUL").text(nodeUL);
|
||||
if (!filled_root) {
|
||||
$("#nodeName").text(redactText(rootName));
|
||||
let nodeDL = scaleNumber(data[0][1].max_throughput[0] * 1000000);
|
||||
let nodeUL = scaleNumber(data[0][1].max_throughput[1] * 1000000);
|
||||
if (nodeDL == "0") nodeDL = "Unlimited";
|
||||
if (nodeUL == "0") nodeUL = "Unlimited";
|
||||
$("#nodeDL").text(nodeDL);
|
||||
$("#nodeUL").text(nodeUL);
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/node_names",
|
||||
data: JSON.stringify(data[0][1].parents),
|
||||
success: (nodeNames) => {
|
||||
console.log(nodeNames);
|
||||
let breadcrumbs = "<nav aria-label='breadcrumb'>";
|
||||
breadcrumbs += "<ol class='breadcrumb'>";
|
||||
for (let i=0; i<data[0][1].parents.length; ++i) {
|
||||
let bcid = data[0][1].parents[i];
|
||||
if (bcid != node) {
|
||||
let n = nodeNames.find(e => e[0] == data[0][1].parents[i])[1];
|
||||
breadcrumbs += "<li class='breadcrumb-item redact'>";
|
||||
breadcrumbs += "<a href='/tree?parent=" + data[0][1].parents[i] + "'>";
|
||||
breadcrumbs += redactText(n);
|
||||
breadcrumbs += "</a></li>";
|
||||
}
|
||||
}
|
||||
breadcrumbs += "<li class='breadcrumb-item active redact' aria-current='page'>";
|
||||
breadcrumbs += redactText(rootName);
|
||||
breadcrumbs += "</li>";
|
||||
breadcrumbs += "</ol>";
|
||||
breadcrumbs += "</nav>";
|
||||
$("#breadcrumbs").html(breadcrumbs);
|
||||
}
|
||||
});
|
||||
filled_root = true;
|
||||
}
|
||||
|
||||
getClients(rootName);
|
||||
|
||||
@ -174,15 +219,15 @@
|
||||
buffers.push(rootName, data[0][1].current_throughput[0] * 8, data[0][1].current_throughput[1] * 8);
|
||||
|
||||
// Build the table & update node buffers
|
||||
let tbl = "<table class='table'>";
|
||||
tbl += "<thead><th>Site</th><th>Limit</th><th>Download</th><th>Upload</th><th>RTT Latency</th></thead>";
|
||||
let tbl = "<table class='table table-striped'>";
|
||||
tbl += "<thead><th>Site</th><th>Limit</th><th>⬇️ DL</th><th>⬆️ UL</th><th>RTT Latency</th></thead>";
|
||||
for (let i = 1; i < data.length; ++i) {
|
||||
let nodeName = data[i][1].name;
|
||||
|
||||
buffers.push(nodeName, data[i][1].current_throughput[0] * 8,data[i][1].current_throughput[1] * 8);
|
||||
buffers.push(nodeName, data[i][1].current_throughput[0] * 8, data[i][1].current_throughput[1] * 8);
|
||||
|
||||
tbl += "<tr>";
|
||||
tbl += "<td style='width: 20%' class='redact'><a href='/tree?parent=" + encodeURI(data[i][0]) + "'>" + redactText(nodeName) + "</a></td>";
|
||||
tbl += "<td class='redact'><a href='/tree?parent=" + encodeURI(data[i][0]) + "'>" + redactText(nodeName) + "</a></td>";
|
||||
if (data[i][1].max_throughput[0] == 0 && data[i][1].max_throughput[1] == 0) {
|
||||
tbl += "<td>No Limit</td>";
|
||||
} else {
|
||||
|
@ -25,10 +25,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/"><i class="fa fa-home"></i> Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<!--<li class="nav-item">
|
||||
<a class="nav-link" href="#"><i class="fa fa-globe"></i> Network Layout</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>
|
||||
@ -39,6 +38,7 @@
|
||||
</div>
|
||||
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<li class="nav-item ms-auto">
|
||||
<a class="nav-link" href="/config"><i class="fa fa-gear"></i> Configuration</a>
|
||||
</li>
|
||||
|
@ -160,10 +160,16 @@ fn handle_bus_requests(
|
||||
BusRequest::RequestLqosEquinixTest => lqos_daht_test::lqos_daht_test(),
|
||||
BusRequest::ValidateShapedDevicesCsv => {
|
||||
validation::validate_shaped_devices_csv()
|
||||
},
|
||||
}
|
||||
BusRequest::GetNetworkMap { parent } => {
|
||||
shaped_devices_tracker::get_one_network_map_layer(*parent)
|
||||
},
|
||||
}
|
||||
BusRequest::TopMapQueues( n_queues ) => {
|
||||
shaped_devices_tracker::get_top_n_root_queues(*n_queues)
|
||||
}
|
||||
BusRequest::GetNodeNamesFromIds(nodes) => {
|
||||
shaped_devices_tracker::map_node_names(nodes)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use log::{error, info, warn};
|
||||
use lqos_bus::BusResponse;
|
||||
use lqos_config::ConfigShapedDevices;
|
||||
use lqos_config::{ConfigShapedDevices, NetworkJsonNode};
|
||||
use lqos_utils::file_watcher::FileWatcher;
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::RwLock;
|
||||
@ -63,4 +63,54 @@ pub fn get_one_network_map_layer(parent_idx: usize) -> BusResponse {
|
||||
} else {
|
||||
BusResponse::Fail("No such node".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_top_n_root_queues(n_queues: usize) -> BusResponse {
|
||||
let net_json = NETWORK_JSON.read();
|
||||
if let Some(parent) = net_json.get_cloned_entry_by_index(0) {
|
||||
let mut nodes = vec![(0, parent)];
|
||||
nodes.extend_from_slice(&net_json.get_cloned_children(0));
|
||||
// Remove the top-level entry for root
|
||||
nodes.remove(0);
|
||||
// Sort by total bandwidth (up + down) descending
|
||||
nodes.sort_by(|a,b| {
|
||||
let total_a = a.1.current_throughput.0 + a.1.current_throughput.1;
|
||||
let total_b = b.1.current_throughput.0 + b.1.current_throughput.1;
|
||||
total_b.cmp(&total_a)
|
||||
});
|
||||
// Summarize everything after n_queues
|
||||
if nodes.len() > n_queues {
|
||||
let mut other_bw = (0, 0);
|
||||
nodes.drain(n_queues ..).for_each(|n| {
|
||||
other_bw.0 += n.1.current_throughput.0;
|
||||
other_bw.1 += n.1.current_throughput.1;
|
||||
});
|
||||
|
||||
nodes.push((0, NetworkJsonNode{
|
||||
name: "Others".into(),
|
||||
max_throughput: (0,0),
|
||||
current_throughput: other_bw,
|
||||
rtts: Vec::new(),
|
||||
parents: Vec::new(),
|
||||
immediate_parent: None,
|
||||
}));
|
||||
}
|
||||
BusResponse::NetworkMap(nodes)
|
||||
} else {
|
||||
BusResponse::Fail("No such node".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_node_names(nodes: &[usize]) -> BusResponse {
|
||||
let mut result = Vec::new();
|
||||
let reader = NETWORK_JSON.read();
|
||||
nodes.iter().for_each(|id| {
|
||||
if let Some(node) = reader.nodes.get(*id) {
|
||||
result.push((
|
||||
*id,
|
||||
node.name.clone(),
|
||||
));
|
||||
}
|
||||
});
|
||||
BusResponse::NodeNames(result)
|
||||
}
|
Loading…
Reference in New Issue
Block a user