mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
Easier to read queue data, now showing graphs for throughput, delays, queue length, etc.
This commit is contained in:
parent
c1df274ae9
commit
418e262b24
@ -1,4 +1,5 @@
|
||||
mod ip_stats;
|
||||
use std::net::IpAddr;
|
||||
use anyhow::Result;
|
||||
pub use ip_stats::{IpMapping, IpStats, XdpPpingResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -19,6 +20,7 @@ pub enum BusRequest {
|
||||
GetCurrentThroughput,
|
||||
GetTopNDownloaders(u32),
|
||||
GetWorstRtt(u32),
|
||||
GetHostCounter,
|
||||
MapIpToFlow {
|
||||
ip_address: String,
|
||||
tc_handle: TcHandle,
|
||||
@ -56,6 +58,7 @@ pub enum BusResponse {
|
||||
packets_per_second: (u64, u64),
|
||||
shaped_bits_per_second: (u64, u64),
|
||||
},
|
||||
HostCounters(Vec<(IpAddr, u64, u64)>),
|
||||
TopDownloaders(Vec<IpStats>),
|
||||
WorstRtt(Vec<IpStats>),
|
||||
MappedIps(Vec<IpMapping>),
|
||||
|
@ -49,6 +49,8 @@ fn rocket() -> _ {
|
||||
unknown_devices::unknown_devices_range,
|
||||
queue_info::raw_queue_by_circuit,
|
||||
queue_info::run_btest,
|
||||
queue_info::circuit_name,
|
||||
queue_info::current_circuit_throughput,
|
||||
config_control::get_nic_list,
|
||||
config_control::get_current_python_config,
|
||||
config_control::get_current_lqosd_config,
|
||||
|
@ -1,8 +1,68 @@
|
||||
use lqos_bus::{BusResponse, BUS_BIND_ADDRESS, BusSession, BusRequest, encode_request, decode_response};
|
||||
use rocket::response::content::RawJson;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::tokio::io::{AsyncWriteExt, AsyncReadExt};
|
||||
use rocket::tokio::net::TcpStream;
|
||||
use crate::cache_control::NoCache;
|
||||
use crate::tracker::SHAPED_DEVICES;
|
||||
use std::net::IpAddr;
|
||||
|
||||
#[get("/api/circuit_name/<circuit_id>")]
|
||||
pub async fn circuit_name(circuit_id: String) -> NoCache<Json<String>> {
|
||||
if let Some(device) = SHAPED_DEVICES.read().devices.iter().find(|d| d.circuit_id == circuit_id) {
|
||||
NoCache::new(Json(device.circuit_name.clone()))
|
||||
} else {
|
||||
let result = "Nameless".to_string();
|
||||
NoCache::new(Json(result))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/api/circuit_throughput/<circuit_id>")]
|
||||
pub async fn current_circuit_throughput(circuit_id: String) -> NoCache<Json<Vec<(String, u64, u64)>>> {
|
||||
let mut result = Vec::new();
|
||||
// Get a list of host counts
|
||||
// This is really inefficient, but I'm struggling to find a better way.
|
||||
// TODO: Fix me up
|
||||
let mut stream = TcpStream::connect(BUS_BIND_ADDRESS).await.unwrap();
|
||||
let test = BusSession {
|
||||
auth_cookie: 1234,
|
||||
requests: vec![
|
||||
BusRequest::GetHostCounter,
|
||||
],
|
||||
};
|
||||
let msg = encode_request(&test).unwrap();
|
||||
stream.write(&msg).await.unwrap();
|
||||
|
||||
// Receive reply
|
||||
let mut buf = Vec::new();
|
||||
let _ = stream.read_to_end(&mut buf).await.unwrap();
|
||||
let reply = decode_response(&buf).unwrap();
|
||||
for msg in reply.responses.iter() {
|
||||
match msg {
|
||||
BusResponse::HostCounters(hosts) => {
|
||||
let devices = SHAPED_DEVICES.read();
|
||||
for (ip, down, up) in hosts.iter() {
|
||||
let lookup = match ip {
|
||||
IpAddr::V4(ip) => ip.to_ipv6_mapped(),
|
||||
IpAddr::V6(ip) => *ip,
|
||||
};
|
||||
if let Some(c) = devices.trie.longest_match(lookup) {
|
||||
if devices.devices[*c.1].circuit_id == circuit_id {
|
||||
result.push((
|
||||
ip.to_string(),
|
||||
*down,
|
||||
*up
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
NoCache::new(Json(result))
|
||||
}
|
||||
|
||||
#[get("/api/raw_queue_by_circuit/<circuit_id>")]
|
||||
pub async fn raw_queue_by_circuit(circuit_id: String) -> NoCache<RawJson<String>> {
|
||||
|
@ -23,7 +23,6 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/"><i class="fa fa-home"></i> Dashboard</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#"><i class="fa fa-globe"></i> Network Layout</a>
|
||||
</li>
|
||||
@ -49,98 +48,146 @@
|
||||
|
||||
<div id="container" style="padding: 4px;">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<div id="raw"></div>
|
||||
<div class="row" style="margin-top: -4px; margin-bottom: 4px;">
|
||||
<div class="col-sm-12 bg-light" style="text-align: center;">
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<span id="circuitName" style="font-weight: bold"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
Queue Memory: <span id="memory"></span><br />
|
||||
Queue Length: <span id="qlen"></span>
|
||||
<div class="col-sm-6">
|
||||
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="pills-home-tab" data-bs-toggle="pill" data-bs-target="#pills-home" type="button" role="tab" aria-controls="pills-home" aria-selected="true">Overview</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="pills-tins-tab" data-bs-toggle="pill" data-bs-target="#pills-tins" type="button" role="tab" aria-controls="pills-profile" aria-selected="false">All Tins</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
Average Delay: <span id="avgDelay"></span><br />
|
||||
Peak Delay: <span id="peakDelay"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
Backlog: <span id="backlog"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 4px">
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Tin 1 (Bulk)</h5>
|
||||
<div id="tinTp_0" style="height: 150px"></div>
|
||||
<div id="tinMd_0" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Tin 2 (Best Effort)</h5>
|
||||
<div id="tinTp_1" style="height: 150px"></div>
|
||||
<div id="tinMd_1" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 4px">
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Tin 3 (Video)</h5>
|
||||
<div id="tinTp_2" style="height: 150px"></div>
|
||||
<div id="tinMd_2" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Tin 4 (Voice)</h5>
|
||||
<div id="tinTp_3" style="height: 150px"></div>
|
||||
<div id="tinMd_3" style="height: 150px"></div>
|
||||
<div class="col-sm-2">
|
||||
<div id="raw"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-content" id="pills-tabContent">
|
||||
<div class="tab-pane fade show active" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab" tabindex="0">
|
||||
|
||||
<!-- Total Throughput and Backlog -->
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Throughput</h5>
|
||||
<div id="throughputGraph" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Backlog</h5>
|
||||
<div id="backlogGraph" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delay and Queue Length -->
|
||||
<div class="row" style="margin-top: 4px;">
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Delays</h5>
|
||||
<div id="delayGraph" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Queue Length</h5>
|
||||
<div id="qlenGraph" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top: 4px;">
|
||||
<div class="col-sm-2">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
Queue Memory: <span id="memory"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="tab-pane fade" id="pills-tins" role="tabpanel" aria-labelledby="pills-tins-tab" tabindex="0">
|
||||
<div class="row" style="margin-top: 4px">
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Tin 1 (Bulk)</h5>
|
||||
<div id="tinTp_0" style="height: 150px"></div>
|
||||
<div id="tinMd_0" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Tin 2 (Best Effort)</h5>
|
||||
<div id="tinTp_1" style="height: 150px"></div>
|
||||
<div id="tinMd_1" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 4px">
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Tin 3 (Video)</h5>
|
||||
<div id="tinTp_2" style="height: 150px"></div>
|
||||
<div id="tinMd_2" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Tin 4 (Voice)</h5>
|
||||
<div id="tinTp_3" style="height: 150px"></div>
|
||||
<div id="tinMd_3" style="height: 150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>Copyright (c) 2022, LibreQoE LLC</footer>
|
||||
|
||||
<script>
|
||||
let throughput = new Object();
|
||||
let throughput_head = 0;
|
||||
|
||||
|
||||
function setX(x, counter) {
|
||||
for (let i=0; i<6; i++) {
|
||||
for (let i=0; i<x.length; i++) {
|
||||
x[i].push(counter);
|
||||
}
|
||||
}
|
||||
|
||||
function setY(y, i, data, tin) {
|
||||
if (data[0] == "None" || data[1] == "None") {
|
||||
y[0].push(0);
|
||||
y[1].push(0);
|
||||
y[2].push(0);
|
||||
y[3].push(0);
|
||||
y[4].push(0);
|
||||
y[5].push(0);
|
||||
for (let j=0; i<y.length; y++) {
|
||||
y[j].push(0);
|
||||
}
|
||||
} else {
|
||||
y[0].push(data[0].Cake.tins[tin].sent_bytes * 8); // Download
|
||||
y[1].push(0.0 - (data[1].Cake.tins[tin].sent_bytes * 8)); // Upload
|
||||
@ -148,6 +195,14 @@
|
||||
y[3].push(data[0].Cake.tins[tin].marks); // Down Marks
|
||||
y[4].push(0.0 - data[1].Cake.tins[tin].drops); // Up Drops
|
||||
y[5].push(0.0 - data[1].Cake.tins[tin].marks); // Up Marks
|
||||
|
||||
// Backlog
|
||||
y[6].push(data[0].Cake.tins[tin].backlog_bytes * 8);
|
||||
y[7].push(0.0 - data[1].Cake.tins[tin].backlog_bytes * 8);
|
||||
|
||||
// Delays
|
||||
y[8].push(data[0].Cake.tins[tin].avg_delay_us);
|
||||
y[9].push(0.0 - data[1].Cake.tins[tin].avg_delay_us);
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,43 +211,60 @@
|
||||
get: (searchParams, prop) => searchParams.get(prop),
|
||||
});
|
||||
if (params.id != null) {
|
||||
// Name the circuit
|
||||
$.get("/api/circuit_name/" + encodeURI(params.id), (data) => {
|
||||
$("#circuitName").text(data);
|
||||
});
|
||||
|
||||
// Fill the raw button
|
||||
$("#raw").html("<a class='btn btn-sm btn-info' href='/api/raw_queue_by_circuit/" + encodeURI(params.id) + "'><i class='fa fa-search'></i> Raw Data</a>");
|
||||
|
||||
// Graphs
|
||||
$.get("/api/raw_queue_by_circuit/" + encodeURI(params.id), (data) => {
|
||||
// Fill Base Information
|
||||
let total_memory = data.current_download.Cake.memory_used + data.current_upload.Cake.memory_used;
|
||||
$("#memory").text(scaleNumber(total_memory));
|
||||
let queue_length = data.current_download.Cake.qlen + data.current_upload.Cake.qlen;
|
||||
$("#qlen").text(scaleNumber(queue_length));
|
||||
let avgDelay = "";
|
||||
let peakDelay = "";
|
||||
let backlog = "";
|
||||
for (let i=0; i<4; i++) {
|
||||
avgDelay += data.current_download.Cake.tins[i].avg_delay_us + " / ";
|
||||
peakDelay += data.current_download.Cake.tins[i].peak_delay_us + " / ";
|
||||
backlog += data.current_download.Cake.tins[i].backlog_bytes + " / ";
|
||||
}
|
||||
avgDelay = avgDelay.substring(0, avgDelay.length - 2) + " us";
|
||||
peakDelay = peakDelay.substring(0, peakDelay.length - 2) + " us";
|
||||
backlog = backlog.substring(0, backlog.length - 2);
|
||||
$("#avgDelay").text(avgDelay);
|
||||
$("#peakDelay").text(peakDelay);
|
||||
$("#backlog").text(backlog);
|
||||
|
||||
// Fill Tin Graphs
|
||||
let backlogX1 = [];
|
||||
let backlogY1 = [];
|
||||
let backlogX2 = [];
|
||||
let backlogY2 = [];
|
||||
let delaysX1 = [];
|
||||
let delaysX2 = [];
|
||||
let delaysY1 = [];
|
||||
let delaysY2 = [];
|
||||
let qlenX1 = [];
|
||||
let qlenY1 = [];
|
||||
let qlenX2 = [];
|
||||
let qlenY2 = [];
|
||||
|
||||
for (let tin=0; tin<4; tin++) {
|
||||
let entries = {
|
||||
x: [[], [], [], [], [], []],
|
||||
y: [[], [], [], [], [], []],
|
||||
x: [[], [], [], [], [], [], [], [], [], []],
|
||||
y: [[], [], [], [], [], [], [], [], [], []],
|
||||
}
|
||||
let counter = 0;
|
||||
for (let i=data.history_head; i<600; i++) {
|
||||
setX(entries.x, counter);
|
||||
setY(entries.y, i, data.history[i], tin);
|
||||
if (tin == 0) {
|
||||
qlenX1.push(counter);
|
||||
qlenX2.push(counter);
|
||||
qlenY1.push(data.history[i][0].Cake.qlen);
|
||||
qlenY2.push(0.0 - data.history[i][1].Cake.qlen);
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
for (let i=0; i<data.history_head; i++) {
|
||||
setX(entries.x, counter);
|
||||
setY(entries.y, i, data.history[i], tin);
|
||||
if (tin == 0) {
|
||||
qlenX1.push(counter);
|
||||
qlenX2.push(counter);
|
||||
qlenY1.push(data.history[i][0].Cake.qlen);
|
||||
qlenY2.push(0.0 - data.history[i][1].Cake.qlen);
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
let graph = document.getElementById("tinTp_" + tin);
|
||||
@ -210,17 +282,122 @@
|
||||
{x: entries.x[5], y:entries.y[5], name: 'Up Marks', type: 'scatter'},
|
||||
];
|
||||
Plotly.newPlot(graph, graph_data, { margin: { l:0,r:0,b:0,t:0,pad:4 }, yaxis: { automargin: true }, xaxis: {automargin: true} });
|
||||
|
||||
backlogX1.push(entries.x[6]);
|
||||
backlogX2.push(entries.x[7]);
|
||||
backlogY1.push(entries.y[6]);
|
||||
backlogY2.push(entries.y[7]);
|
||||
delaysX1.push(entries.x[8]);
|
||||
delaysX2.push(entries.x[9]);
|
||||
delaysY1.push(entries.y[8]);
|
||||
delaysY2.push(entries.y[9]);
|
||||
}
|
||||
let graph = document.getElementById("backlogGraph");
|
||||
let graph_data = [
|
||||
{x: backlogX1[0], y:backlogY1[0], type: 'scatter', name: 'Tin 0 Down'},
|
||||
{x: backlogX2[0], y:backlogY2[0], type: 'scatter', name: 'Tin 0 Up'},
|
||||
{x: backlogX1[1], y:backlogY1[1], type: 'scatter', name: 'Tin 1 Down'},
|
||||
{x: backlogX2[1], y:backlogY2[1], type: 'scatter', name: 'Tin 1 Up'},
|
||||
{x: backlogX1[2], y:backlogY1[2], type: 'scatter', name: 'Tin 2 Down'},
|
||||
{x: backlogX2[2], y:backlogY2[2], type: 'scatter', name: 'Tin 2 Up'},
|
||||
{x: backlogX1[3], y:backlogY1[3], type: 'scatter', name: 'Tin 3 Down'},
|
||||
{x: backlogX2[3], y:backlogY2[3], type: 'scatter', name: 'Tin 3 Up'},
|
||||
];
|
||||
Plotly.newPlot(graph, graph_data, { margin: { l:0,r:0,b:0,t:0,pad:4 }, yaxis: { automargin: true }, xaxis: {automargin: true} });
|
||||
|
||||
graph = document.getElementById("delayGraph");
|
||||
graph_data = [
|
||||
{x: delaysX1[0], y:delaysY1[0], type: 'scatter', name: 'Tin 0 Down'},
|
||||
{x: delaysX2[0], y:delaysY2[0], type: 'scatter', name: 'Tin 0 Up'},
|
||||
{x: delaysX1[1], y:delaysY1[1], type: 'scatter', name: 'Tin 1 Down'},
|
||||
{x: delaysX2[1], y:delaysY2[1], type: 'scatter', name: 'Tin 1 Up'},
|
||||
{x: delaysX1[2], y:delaysY1[2], type: 'scatter', name: 'Tin 2 Down'},
|
||||
{x: delaysX2[2], y:delaysY2[2], type: 'scatter', name: 'Tin 2 Up'},
|
||||
{x: delaysX1[3], y:delaysY1[3], type: 'scatter', name: 'Tin 3 Down'},
|
||||
{x: delaysX2[3], y:delaysY2[3], type: 'scatter', name: 'Tin 3 Up'},
|
||||
];
|
||||
Plotly.newPlot(graph, graph_data, { margin: { l:0,r:0,b:0,t:0,pad:4 }, yaxis: { automargin: true }, xaxis: {automargin: true} });
|
||||
|
||||
graph = document.getElementById("qlenGraph");
|
||||
graph_data = [
|
||||
{x: qlenX1, y:qlenY1, type: 'scatter', name: 'Down'},
|
||||
{x: qlenX2, y:qlenY2, type: 'scatter', name: 'Up'},
|
||||
];
|
||||
Plotly.newPlot(graph, graph_data, { margin: { l:0,r:0,b:0,t:0,pad:4 }, yaxis: { automargin: true }, xaxis: {automargin: true} });
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(pollQueue, 1000);
|
||||
}
|
||||
|
||||
function getThroughput() {
|
||||
const params = new Proxy(new URLSearchParams(window.location.search), {
|
||||
get: (searchParams, prop) => searchParams.get(prop),
|
||||
});
|
||||
if (params.id != null) {
|
||||
$.get("/api/circuit_throughput/" + encodeURI(params.id), (data) => {
|
||||
for (let i=0; i<data.length; i++) {
|
||||
let ip = data[i][0];
|
||||
let down = data[i][1];
|
||||
let up = data[i][2];
|
||||
|
||||
if (throughput[ip] == null) {
|
||||
throughput[ip] = {
|
||||
down: [],
|
||||
up: [],
|
||||
};
|
||||
for (let j=0; j<300; j++) {
|
||||
throughput[ip].down.push(0);
|
||||
throughput[ip].up.push(0);
|
||||
}
|
||||
}
|
||||
throughput[ip].down[throughput_head] = down * 8;
|
||||
throughput[ip].up[throughput_head] = up * 8;
|
||||
}
|
||||
|
||||
// Build the graph
|
||||
let graph = document.getElementById("throughputGraph");
|
||||
let graph_data = [];
|
||||
for (const ip in throughput) {
|
||||
//console.log(ip);
|
||||
let xUp = [];
|
||||
let xDown = [];
|
||||
let yUp = [];
|
||||
let yDown = [];
|
||||
let counter = 0;
|
||||
for (let i=throughput_head; i<300; i++) {
|
||||
xUp.push(counter);
|
||||
xDown.push(counter);
|
||||
yUp.push(0.0 - throughput[ip].up[i]);
|
||||
yDown.push(throughput[ip].down[i]);
|
||||
counter++;
|
||||
}
|
||||
for (let i=0; i<throughput_head; i++) {
|
||||
xUp.push(counter);
|
||||
xDown.push(counter);
|
||||
yUp.push(0.0 - throughput[ip].up[i]);
|
||||
yDown.push(throughput[ip].down[i]);
|
||||
counter++;
|
||||
}
|
||||
graph_data.push({x: xDown, y: yDown, name: ip + " Down", type: 'scatter'});
|
||||
graph_data.push({x: xUp, y: yUp, name: ip + " Up", type: 'scatter'});
|
||||
}
|
||||
Plotly.newPlot(graph, graph_data, { margin: { l:0,r:0,b:0,t:0,pad:4 }, yaxis: { automargin: true }, xaxis: {automargin: true} });
|
||||
throughput_head += 1;
|
||||
if (throughput_head >= 300) {
|
||||
throughput_head = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(getThroughput, 1000);
|
||||
}
|
||||
|
||||
function start() {
|
||||
colorReloadButton();
|
||||
updateHostCounts();
|
||||
pollQueue();
|
||||
getThroughput();
|
||||
}
|
||||
|
||||
$(document).ready(start);
|
||||
|
@ -89,6 +89,7 @@ async fn main() -> Result<()> {
|
||||
BusRequest::GetCurrentThroughput => {
|
||||
throughput_tracker::current_throughput()
|
||||
}
|
||||
BusRequest::GetHostCounter => throughput_tracker::host_counters(),
|
||||
BusRequest::GetTopNDownloaders(n) => throughput_tracker::top_n(*n),
|
||||
BusRequest::GetWorstRtt(n) => throughput_tracker::worst_n(*n),
|
||||
BusRequest::MapIpToFlow {
|
||||
|
@ -28,6 +28,7 @@ pub(crate) fn make_queue_diff(previous: &QueueType, current: &QueueType) -> Resu
|
||||
pub struct CakeDiff {
|
||||
pub bytes: u64,
|
||||
pub packets: u64,
|
||||
pub qlen: u64,
|
||||
pub tins: Vec<CakeDiffTin>,
|
||||
}
|
||||
|
||||
@ -37,6 +38,7 @@ pub struct CakeDiffTin {
|
||||
pub backlog_bytes: u64,
|
||||
pub drops: u64,
|
||||
pub marks: u64,
|
||||
pub avg_delay_us: u64,
|
||||
}
|
||||
|
||||
fn cake_diff(previous: &QueueType, current: &QueueType) -> Result<QueueDiff> {
|
||||
@ -47,14 +49,16 @@ fn cake_diff(previous: &QueueType, current: &QueueType) -> Result<QueueDiff> {
|
||||
//println!("{} - {} = {}", new.sent_bytes, prev.sent_bytes, new.sent_bytes -prev.sent_bytes);
|
||||
CakeDiffTin {
|
||||
sent_bytes: new.sent_bytes - prev.sent_bytes,
|
||||
backlog_bytes: new.backlog_bytes - prev.backlog_bytes,
|
||||
backlog_bytes: new.backlog_bytes,
|
||||
drops: new.drops - prev.drops,
|
||||
marks: new.ecn_marks - prev.ecn_marks,
|
||||
avg_delay_us: new.avg_delay_us,
|
||||
}
|
||||
}).collect();
|
||||
return Ok(QueueDiff::Cake(CakeDiff{
|
||||
bytes: new.bytes - prev.bytes,
|
||||
packets: new.packets - prev.packets,
|
||||
qlen: new.qlen,
|
||||
tins,
|
||||
}));
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ pub(crate) struct TcCake {
|
||||
target_us: u64,
|
||||
interval_us: u64,
|
||||
peak_delay_us: u64,
|
||||
avg_delay_us: u64,
|
||||
pub(crate) avg_delay_us: u64,
|
||||
base_delay_us: u64,
|
||||
sent_packets: u64,
|
||||
way_indirect_hits: u64,
|
||||
|
@ -54,6 +54,18 @@ pub fn current_throughput() -> BusResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host_counters() -> BusResponse {
|
||||
let mut result = Vec::new();
|
||||
let tp = THROUGHPUT_TRACKER.read();
|
||||
tp.raw_data.iter().for_each(|(k,v)| {
|
||||
let ip = k.as_ip();
|
||||
let (down, up) = v.bytes_per_second;
|
||||
result.push((ip, down, up));
|
||||
});
|
||||
BusResponse::HostCounters(result)
|
||||
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn retire_check(cycle: u64, recent_cycle: u64) -> bool {
|
||||
cycle < recent_cycle + RETIRE_AFTER_SECONDS
|
||||
|
Loading…
Reference in New Issue
Block a user