diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index ce1e9a858..faa1a853d 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -280,6 +280,30 @@ pub struct UserCredentials { pub password: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct SystemStats { + pub cpu: f32, // in % + pub mem_used: f64, // in MB + pub mem_total: f64, // in MB + pub disk: DiskUsage, + pub networks: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DiskUsage { + pub used: f64, // in GB + pub total: f64, // in GB + pub read: f64, // in kB + pub write: f64, // in kB +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SystemNetwork { + pub name: String, + pub recieved: f64, // in kB + pub transmitted: f64, // in kB +} + #[derive(Serialize, Deserialize, Debug, Display, EnumString, PartialEq, Hash, Eq, Clone, Copy)] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] diff --git a/periphery/src/api/stats.rs b/periphery/src/api/stats.rs index 62be7c711..c6f666fd0 100644 --- a/periphery/src/api/stats.rs +++ b/periphery/src/api/stats.rs @@ -1,24 +1,92 @@ -use std::sync::Arc; +use std::{ + path::Path, + sync::{Arc, RwLock}, +}; -use axum::{Router, Extension}; +use axum::{routing::get, Extension, Json, Router}; +use sysinfo::{CpuExt, DiskExt, NetworkExt, ProcessExt, ProcessRefreshKind, SystemExt}; +use types::{DiskUsage, SystemNetwork, SystemStats}; pub fn router() -> Router { - Router::new() - - .layer(StatsClient::extension()) + Router::new() + .route( + "/system", + get(|Extension(sys): StatsExtension| async move { + let stats = sys.write().unwrap().get_stats(); + Json(stats) + }), + ) + .layer(StatsClient::extension()) } -type StatsExtension = Extension>; +type StatsExtension = Extension>>; struct StatsClient { - client: sysinfo::System, + sys: sysinfo::System, } +const BYTES_PER_GB: f64 = 1073741824.0; +const BYTES_PER_KB: f64 = 1024.0; + impl StatsClient { - pub fn extension() -> StatsExtension { - let client = StatsClient { - client: sysinfo::System::default(), - }; - Extension(Arc::new(client)) - } -} \ No newline at end of file + pub fn extension() -> StatsExtension { + let client = StatsClient { + sys: sysinfo::System::new_all(), + }; + Extension(Arc::new(RwLock::new(client))) + } + + pub fn get_stats(&mut self) -> SystemStats { + self.sys.refresh_cpu(); + self.sys.refresh_memory(); + SystemStats { + cpu: self.sys.global_cpu_info().cpu_usage(), + mem_used: self.sys.used_memory() as f64 / BYTES_PER_GB, + mem_total: self.sys.total_memory() as f64 / BYTES_PER_GB, + disk: self.get_disk_usage(), + networks: self.get_networks(), + } + } + + fn get_networks(&mut self) -> Vec { + self.sys.refresh_networks(); + self.sys + .networks() + .into_iter() + .map(|(name, n)| SystemNetwork { + name: name.clone(), + recieved: n.received() as f64 / BYTES_PER_KB, + transmitted: n.transmitted() as f64 / BYTES_PER_KB, + }) + .filter(|n| n.recieved > 0.0 || n.transmitted > 0.0) + .collect() + } + + fn get_disk_usage(&mut self) -> DiskUsage { + self.sys.refresh_disks(); + let mut free = 0.0; + let mut total = 0.0; + for disk in self.sys.disks() { + if disk.mount_point() == Path::new("/") { + total += disk.total_space() as f64 / BYTES_PER_GB; + free += disk.available_space() as f64 / BYTES_PER_GB; + } + } + let used = total - free; + self.sys + .refresh_processes_specifics(ProcessRefreshKind::new().with_disk_usage()); + let mut read = 0.0; + let mut write = 0.0; + for (_, process) in self.sys.processes() { + let disk_usage = process.disk_usage(); + read += disk_usage.read_bytes as f64 / BYTES_PER_KB; + write += disk_usage.written_bytes as f64 / BYTES_PER_KB; + } + DiskUsage { + used, + total, + read, + write, + } + } +} diff --git a/periphery/src/main.rs b/periphery/src/main.rs index 2a84845e6..3211a4265 100644 --- a/periphery/src/main.rs +++ b/periphery/src/main.rs @@ -14,7 +14,9 @@ use api::*; async fn main() { let (port, secrets) = config::load(); - let app = Router::new().nest("/container", container::router()); + let app = Router::new() + .nest("/container", container::router()) + .nest("/stats", stats::router()); println!("starting montior periphery on port {port}");