RTT is per-host aware.

This commit is contained in:
Herbert Wolverson 2023-04-26 16:38:23 +00:00
parent 84a3709a59
commit 1e806ebdd9
7 changed files with 75 additions and 185 deletions

View File

@ -1,122 +0,0 @@
use crate::submissions::get_org_details;
use axum::extract::ws::{WebSocket, Message};
use chrono::{DateTime, FixedOffset, Utc};
use influxdb2::{models::Query, Client, FromDataPoint};
use pgdb::sqlx::{Pool, Postgres};
use serde::Serialize;
#[derive(Debug, FromDataPoint)]
pub struct BitsAndPackets {
direction: String,
host_id: String,
min: f64,
max: f64,
avg: f64,
time: DateTime<FixedOffset>,
}
impl Default for BitsAndPackets {
fn default() -> Self {
Self {
direction: "".to_string(),
host_id: "".to_string(),
min: 0.0,
max: 0.0,
avg: 0.0,
time: DateTime::<Utc>::MIN_UTC.into(),
}
}
}
#[derive(Debug, FromDataPoint)]
pub struct Rtt {
host_id: String,
min: f64,
max: f64,
avg: f64,
time: DateTime<FixedOffset>,
}
impl Default for Rtt {
fn default() -> Self {
Self {
host_id: "".to_string(),
min: 0.0,
max: 0.0,
avg: 0.0,
time: DateTime::<Utc>::MIN_UTC.into(),
}
}
}
#[derive(Serialize)]
struct Packets {
value: f64,
date: String,
l: f64,
u: f64,
}
#[derive(Serialize)]
struct PacketChart {
msg: String,
down: Vec<Packets>,
up: Vec<Packets>,
}
#[derive(Serialize)]
struct RttChart {
msg: String,
data: Vec<Packets>,
histo: Vec<u64>,
}
pub async fn rtt(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str) {
if let Some(org) = get_org_details(cnn, key).await {
let influx_url = format!("http://{}:8086", org.influx_host);
let client = Client::new(influx_url, &org.influx_org, &org.influx_token);
let qs = format!(
"from(bucket: \"{}\")
|> range(start: -5m)
|> filter(fn: (r) => r[\"_measurement\"] == \"rtt\")
|> filter(fn: (r) => r[\"organization_id\"] == \"{}\")
|> aggregateWindow(every: 10s, fn: mean, createEmpty: false)
|> yield(name: \"last\")",
org.influx_bucket, org.key
);
let query = Query::new(qs);
let rows = client.query::<Rtt>(Some(query)).await;
match rows {
Err(e) => {
tracing::error!("Error querying InfluxDB: {}", e);
}
Ok(rows) => {
// Parse and send the data
//println!("{rows:?}");
let mut data = Vec::new();
let mut histo = vec![0; 20];
for row in rows
.iter()
{
data.push(Packets {
value: row.avg,
date: row.time.format("%H:%M:%S").to_string(),
l: row.min,
u: row.max - row.min,
});
let bucket = u64::min(19, (row.avg / 200.0) as u64);
histo[bucket as usize] += 1;
}
// Send it
let chart = RttChart { msg: "rttChart".to_string(), data, histo };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
}
}
}
}

View File

@ -4,10 +4,9 @@ use axum::{
};
use pgdb::sqlx::{Pool, Postgres};
use serde_json::Value;
use crate::web::wss::queries::{send_packets_for_all_nodes, send_throughput_for_all_nodes};
use crate::web::wss::queries::{send_packets_for_all_nodes, send_throughput_for_all_nodes, send_rtt_for_all_nodes};
mod login;
mod nodes;
mod dashboard;
mod queries;
pub async fn ws_handler(ws: WebSocketUpgrade, State(state): State<Pool<Postgres>>) -> impl IntoResponse {
@ -69,7 +68,7 @@ async fn handle_socket(mut socket: WebSocket, cnn: Pool<Postgres>) {
}
"rttChart" => {
if let Some(credentials) = &credentials {
dashboard::rtt(cnn.clone(), &mut socket, &credentials.license_key).await;
let _ = send_rtt_for_all_nodes(cnn.clone(), &mut socket, &credentials.license_key).await;
} else {
log::info!("Throughput requested but no credentials provided");
}

View File

@ -3,5 +3,7 @@
mod packet_counts;
mod throughput;
mod rtt;
pub use packet_counts::send_packets_for_all_nodes;
pub use throughput::send_throughput_for_all_nodes;
pub use throughput::send_throughput_for_all_nodes;
pub use rtt::send_rtt_for_all_nodes;

View File

@ -61,7 +61,7 @@ export class PacketsChart implements Component {
type: "line",
data: u,
symbol: 'none',
stack: 'confidence-band' + node.node_id,
stack: 'confidence-band-' + node.node_id,
lineStyle: {
opacity: 0
},
@ -105,7 +105,7 @@ export class PacketsChart implements Component {
type: "line",
data: u,
symbol: 'none',
stack: 'confidence-band' + node.node_id,
stack: 'confidence-band-' + node.node_id,
lineStyle: {
opacity: 0
},
@ -125,7 +125,6 @@ export class PacketsChart implements Component {
series.push(val);
}
if (!this.chartMade) {
this.myChart.hideLoading();
var option: echarts.EChartsOption;

View File

@ -5,10 +5,6 @@ import * as echarts from 'echarts';
export class RttChart implements Component {
div: HTMLElement;
myChart: echarts.ECharts;
download: any;
downloadMin: any;
downloadMax: any;
x: any;
chartMade: boolean = false;
constructor() {
@ -26,16 +22,62 @@ export class RttChart implements Component {
onmessage(event: any): void {
if (event.msg == "rttChart") {
//console.log(event);
this.download = [];
this.downloadMin = [];
this.downloadMax = [];
this.x = [];
for (let i = 0; i < event.data.length; i++) {
this.download.push(event.data[i].value);
this.downloadMin.push(event.data[i].l);
this.downloadMax.push(event.data[i].u);
this.x.push(event.data[i].date);
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
// providing upload and download banding per node.
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
legend.push(node.node_name);
//console.log(node);
let d: number[] = [];
let u: number[] = [];
let l: number[] = [];
for (let j=0; j<node.rtt.length; j++) {
if (first) x.push(node.rtt[j].date);
d.push(node.rtt[j].value);
u.push(node.rtt[j].u);
l.push(node.rtt[j].l);
}
if (first) first = false;
let min: echarts.SeriesOption = {
name: "L",
type: "line",
data: l,
symbol: 'none',
stack: 'confidence-band-' + node.node_id,
lineStyle: {
opacity: 0
},
};
let max: echarts.SeriesOption = {
name: "U",
type: "line",
data: u,
symbol: 'none',
stack: 'confidence-band-' + node.node_id,
lineStyle: {
opacity: 0
},
areaStyle: {
color: '#ccc'
},
};
let val: echarts.SeriesOption = {
name: node.node_name,
type: "line",
data: d,
symbol: 'none',
};
series.push(min);
series.push(max);
series.push(val);
}
if (!this.chartMade) {
@ -44,48 +86,21 @@ export class RttChart implements Component {
this.myChart.setOption<echarts.EChartsOption>(
(option = {
title: { text: "TCP Round-Trip Time" },
legend: {
orient: "horizontal",
right: 10,
top: "bottom",
data: legend,
},
xAxis: {
type: 'category',
data: this.x,
data: x,
},
yAxis: {
type: 'value',
name: 'ms',
},
series: [
{
name: "L",
type: "line",
data: this.downloadMin,
symbol: 'none',
stack: 'confidence-band',
lineStyle: {
opacity: 0
},
},
{
name: "U",
type: "line",
data: this.downloadMax,
symbol: 'none',
stack: 'confidence-band',
lineStyle: {
opacity: 0
},
areaStyle: {
color: '#ccc'
},
},
{
name: "Download",
type: "line",
data: this.download,
symbol: 'none',
itemStyle: {
color: '#333'
},
},
]
series: series
})
);
option && this.myChart.setOption(option);

View File

@ -26,8 +26,8 @@ export class RttHisto implements Component {
//console.log(event);
this.download = [];
this.x = [];
for (let i = 0; i < event.histo.length; i++) {
this.download.push(event.histo[i]);
for (let i = 0; i < event.histogram.length; i++) {
this.download.push(event.histogram[i]);
this.x.push(i * 10);
}
@ -50,9 +50,6 @@ export class RttHisto implements Component {
name: "RTT",
type: "bar",
data: this.download,
itemStyle: {
color: '#333'
},
},
]
})

View File

@ -61,7 +61,7 @@ export class ThroughputChart implements Component {
type: "line",
data: u,
symbol: 'none',
stack: 'confidence-band' + node.node_id,
stack: 'confidence-band-' + node.node_id,
lineStyle: {
opacity: 0
},
@ -105,7 +105,7 @@ export class ThroughputChart implements Component {
type: "line",
data: u,
symbol: 'none',
stack: 'confidence-band' + node.node_id,
stack: 'confidence-band-' + node.node_id,
lineStyle: {
opacity: 0
},