crimes to async

This commit is contained in:
Robin Appelman 2021-03-24 18:17:13 +01:00
commit cd6e103337
5 changed files with 107 additions and 64 deletions

27
Cargo.lock generated
View file

@ -1241,9 +1241,11 @@ dependencies = [
"color-eyre",
"ctrlc",
"dotenv",
"futures-lite",
"futures-util",
"heim",
"libzetta",
"parse-display",
"tokio",
"warp",
]
@ -1254,6 +1256,31 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]]
name = "parse-display"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7271152b3c46c07c729698e7a5248e2744466b3446d222c97a0b1315925a97b1"
dependencies = [
"once_cell",
"parse-display-derive",
"regex",
]
[[package]]
name = "parse-display-derive"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6a9f3e41b237b77c99c09686481c235964ff5878229412b226c451f3e809f4f"
dependencies = [
"once_cell",
"proc-macro2",
"quote",
"regex",
"regex-syntax",
"syn",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"

View file

@ -12,4 +12,6 @@ warp = "0.3"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
ctrlc = { version = "3", features = ["termination"] }
dotenv = "0.15"
futures-util = "0.3"
futures-util = "0.3"
futures-lite = "1"
parse-display = "0.4"

View file

@ -1,21 +1,20 @@
use color_eyre::eyre::WrapErr;
use color_eyre::Result;
use futures_util::future;
use futures_util::stream::{Stream, StreamExt};
use futures_util::{future, TryStreamExt};
use heim::sensors::TemperatureSensor;
use heim::units::{information, ratio, thermodynamic_temperature};
use parse_display::Display;
use std::collections::HashMap;
use std::time::Duration;
use tokio::time::sleep;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Hash, Eq, PartialEq, Display)]
#[display(style = "lowercase")]
pub enum TemperatureLabel {
CPU,
}
#[derive(Debug, Clone)]
pub struct Temperature {
pub sensor: TemperatureLabel,
pub temperature: f32,
}
#[derive(Debug, Clone)]
pub struct Memory {
pub total: u64,
@ -34,26 +33,28 @@ pub struct NetworkStats {
pub struct Heim {}
impl Heim {
#[allow(dead_code)]
pub async fn temperatures() -> Result<Vec<Temperature>> {
let mut temperatures = Vec::new();
let results: Vec<_> = heim::sensors::temperatures().try_collect().await?;
// let results: Vec<TemperatureSensor> = Vec::new();
// pin_mut!(results);
for sensor in results {
if let Some(temp) = match (sensor.unit(), sensor.label()) {
("k10temp", Some("Tdie")) => Some(Temperature {
sensor: TemperatureLabel::CPU,
temperature: sensor
.current()
.get::<thermodynamic_temperature::degree_celsius>(),
}),
_ => None,
} {
temperatures.push(temp);
}
}
Ok(temperatures)
pub async fn temperatures(&self) -> Result<HashMap<TemperatureLabel, f32>> {
// ugly workaround problems between async-fs and tokio
let results = tokio::task::spawn_blocking(|| {
futures_lite::future::block_on(
heim::sensors::temperatures()
.collect::<Vec<Result<TemperatureSensor, heim::Error>>>(),
)
})
.await
.wrap_err("Failed to resolve future")?
.into_iter()
.filter_map(|result| result.ok())
.filter_map(|sensor| match (sensor.unit(), sensor.label()) {
("k10temp", Some("Tdie")) => Some((
TemperatureLabel::CPU,
sensor
.current()
.get::<thermodynamic_temperature::degree_celsius>(),
)),
_ => None,
});
Ok(results.collect())
}
pub async fn memory(&self) -> Result<Memory> {

View file

@ -1,11 +1,12 @@
mod heim;
mod zfs;
use crate::heim::{Heim, Memory, NetworkStats};
use crate::heim::{Heim, Memory, NetworkStats, TemperatureLabel};
use crate::zfs::{ZfsPool, ZFS};
use color_eyre::{Report, Result};
use futures_util::stream::StreamExt;
use futures_util::{pin_mut, try_join};
use std::collections::HashMap;
use std::fmt::Write;
use warp::reject::Reject;
use warp::{Filter, Rejection};
@ -15,6 +16,7 @@ struct ReportRejection(Report);
impl From<Report> for ReportRejection {
fn from(report: Report) -> Self {
eprintln!("{:#}", report);
ReportRejection(report)
}
}
@ -22,16 +24,24 @@ impl From<Report> for ReportRejection {
impl Reject for ReportRejection {}
async fn get_metrics(heim: Heim, zfs: ZFS) -> Result<String, ReportRejection> {
let (hostname, pools, cpu, memory, network): (String, Vec<ZfsPool>, f32, Memory, _) = try_join! {
let (hostname, pools, cpu, memory, network, temperatures): (
String,
Vec<ZfsPool>,
f32,
Memory,
_,
HashMap<TemperatureLabel, f32>,
) = try_join! {
heim.hostname(),
zfs.pools(),
heim.cpu_usage(),
heim.memory(),
heim.network_stats(),
heim.temperatures(),
}?;
pin_mut!(network);
let mut result = String::with_capacity(256);
writeln!(&mut result, "cpu_usage{{host=\"{}\"}} {}", hostname, cpu).ok();
writeln!(&mut result, "cpu_usage{{host=\"{}\"}} {:.1}", hostname, cpu).ok();
writeln!(
&mut result,
"memory_total{{host=\"{}\"}} {}",
@ -65,31 +75,30 @@ async fn get_metrics(heim: Heim, zfs: ZFS) -> Result<String, ReportRejection> {
.ok();
}
while let Some(network) = network.next().await {
let network: NetworkStats = network;
if network.bytes_received > 0 || network.bytes_sent > 0 {
let network: NetworkStats = network;
writeln!(
&mut result,
"net_sent{{host=\"{}\", network=\"{}\"}} {}",
hostname, network.interface, network.bytes_sent
)
.ok();
writeln!(
&mut result,
"net_received{{host=\"{}\", network=\"{}\"}} {}",
hostname, network.interface, network.bytes_received
)
.ok();
}
}
for (label, temp) in temperatures {
writeln!(
&mut result,
"net_sent{{host=\"{}\", network=\"{}\"}} {}",
hostname, network.interface, network.bytes_sent
)
.ok();
writeln!(
&mut result,
"net_received{{host=\"{}\", network=\"{}\"}} {}",
hostname, network.interface, network.bytes_received
"temperature{{host=\"{}\", sensor=\"{}\"}} {:.1}",
hostname, label, temp
)
.ok();
}
// haunted ↓
// for temperature in Heim::temperatures().await? {
// match temperature.sensor {
// TemperatureLabel::CPU => writeln!(
// &mut result,
// "temperature{{host=\"{}\", sensor=\"cpu\"}} {}",
// hostname, temperature.temperature
// )
// .ok(),
// };
// }
Result::<_, ReportRejection>::Ok(result)
}

View file

@ -1,5 +1,6 @@
use color_eyre::Result;
use libzetta::zpool::{ZpoolEngine, ZpoolOpen3};
use tokio::task::spawn_blocking;
#[derive(Clone, Debug)]
pub struct ZfsPool {
@ -21,18 +22,21 @@ impl Default for ZFS {
}
impl ZFS {
pub async fn pools(&self) -> Result<Vec<ZfsPool>> {
let pools = self.engine.all()?;
pools
.into_iter()
.map(|pool| {
let props = self.engine.read_properties(pool.name())?;
Ok(ZfsPool {
name: pool.name().to_string(),
size: *props.size(),
free: *props.size() * (*props.capacity() as usize) / 100,
pub async fn pools(self) -> Result<Vec<ZfsPool>> {
spawn_blocking(move || {
let pools = self.engine.all()?;
pools
.into_iter()
.map(|pool| {
let props = self.engine.read_properties(pool.name())?;
Ok(ZfsPool {
name: pool.name().to_string(),
size: *props.size(),
free: *props.size() * (*props.capacity() as usize) / 100,
})
})
})
.collect()
.collect()
})
.await?
}
}