mirror of
https://codeberg.org/icewind/palantir.git
synced 2026-06-03 18:24:08 +02:00
extract to lib:
This commit is contained in:
parent
490398318a
commit
0b77b8d6a3
6 changed files with 262 additions and 264 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -752,6 +752,12 @@ dependencies = [
|
||||||
"want",
|
"want",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iai"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|
@ -1034,6 +1040,7 @@ dependencies = [
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"heim",
|
"heim",
|
||||||
|
"iai",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parse-display",
|
"parse-display",
|
||||||
"regex",
|
"regex",
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,6 @@ futures-lite = "1"
|
||||||
parse-display = "0.4"
|
parse-display = "0.4"
|
||||||
regex = { version = "1", default-features = false }
|
regex = { version = "1", default-features = false }
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
iai = "0.1"
|
||||||
22
src/heim.rs
22
src/heim.rs
|
|
@ -38,16 +38,11 @@ pub struct DiskUsage {
|
||||||
pub free: u64,
|
pub free: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
pub async fn temperatures() -> Result<HashMap<TemperatureLabel, f32>> {
|
||||||
pub struct Heim {}
|
|
||||||
|
|
||||||
impl Heim {
|
|
||||||
pub async fn temperatures(&self) -> Result<HashMap<TemperatureLabel, f32>> {
|
|
||||||
// ugly workaround problems between async-fs and tokio
|
// ugly workaround problems between async-fs and tokio
|
||||||
let results = tokio::task::spawn_blocking(|| {
|
let results = tokio::task::spawn_blocking(|| {
|
||||||
futures_lite::future::block_on(
|
futures_lite::future::block_on(
|
||||||
heim::sensors::temperatures()
|
heim::sensors::temperatures().collect::<Vec<Result<TemperatureSensor, heim::Error>>>(),
|
||||||
.collect::<Vec<Result<TemperatureSensor, heim::Error>>>(),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|
@ -66,7 +61,7 @@ impl Heim {
|
||||||
Ok(results.collect())
|
Ok(results.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn memory(&self) -> Result<Memory> {
|
pub async fn memory() -> Result<Memory> {
|
||||||
let memory = heim::memory::memory().await?;
|
let memory = heim::memory::memory().await?;
|
||||||
Ok(Memory {
|
Ok(Memory {
|
||||||
total: memory.total().get::<information::byte>(),
|
total: memory.total().get::<information::byte>(),
|
||||||
|
|
@ -75,12 +70,12 @@ impl Heim {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn cpu_time(&self) -> Result<f64> {
|
pub async fn cpu_time() -> Result<f64> {
|
||||||
let time = time().await?;
|
let time = time().await?;
|
||||||
Ok(time.user().get::<time::second>() + time.system().get::<time::second>())
|
Ok(time.user().get::<time::second>() + time.system().get::<time::second>())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn network_stats(&self) -> Result<impl Stream<Item = IOStats>> {
|
pub async fn network_stats() -> Result<impl Stream<Item = IOStats>> {
|
||||||
let networks = heim::net::io_counters().await?;
|
let networks = heim::net::io_counters().await?;
|
||||||
Ok(networks
|
Ok(networks
|
||||||
.filter_map(|network| future::ready(network.ok()))
|
.filter_map(|network| future::ready(network.ok()))
|
||||||
|
|
@ -92,11 +87,11 @@ impl Heim {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn hostname(&self) -> Result<String> {
|
pub async fn hostname() -> Result<String> {
|
||||||
Ok(heim::host::platform().await?.hostname().to_string())
|
Ok(heim::host::platform().await?.hostname().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn disk_stats(&self) -> Result<impl Stream<Item = IOStats>> {
|
pub async fn disk_stats() -> Result<impl Stream<Item = IOStats>> {
|
||||||
static DISK_REGEX: Lazy<Regex> =
|
static DISK_REGEX: Lazy<Regex> =
|
||||||
Lazy::new(|| Regex::new(r"^([sv]d[a-z]+|nvme\dn\d)$").unwrap());
|
Lazy::new(|| Regex::new(r"^([sv]d[a-z]+|nvme\dn\d)$").unwrap());
|
||||||
let disks = heim::disk::io_counters().await?;
|
let disks = heim::disk::io_counters().await?;
|
||||||
|
|
@ -118,7 +113,7 @@ impl Heim {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn disk_usage(&self) -> Result<impl Stream<Item = DiskUsage>> {
|
pub async fn disk_usage() -> Result<impl Stream<Item = DiskUsage>> {
|
||||||
Ok(heim::disk::partitions_physical()
|
Ok(heim::disk::partitions_physical()
|
||||||
.await?
|
.await?
|
||||||
.filter_map(|result| future::ready(result.ok()))
|
.filter_map(|result| future::ready(result.ok()))
|
||||||
|
|
@ -136,4 +131,3 @@ impl Heim {
|
||||||
free: usage.free().get::<information::byte>(),
|
free: usage.free().get::<information::byte>(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
133
src/lib.rs
Normal file
133
src/lib.rs
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
pub mod heim;
|
||||||
|
mod zfs;
|
||||||
|
|
||||||
|
use crate::heim::*;
|
||||||
|
use crate::zfs::pools;
|
||||||
|
use color_eyre::Result;
|
||||||
|
use futures_util::stream::StreamExt;
|
||||||
|
use futures_util::{pin_mut, try_join};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
pub async fn get_metrics() -> Result<String> {
|
||||||
|
let (hostname, pools, cpu, memory, network, temperatures, disks, disk_usage): (
|
||||||
|
String,
|
||||||
|
Vec<DiskUsage>,
|
||||||
|
f64,
|
||||||
|
Memory,
|
||||||
|
_,
|
||||||
|
HashMap<TemperatureLabel, f32>,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
) = try_join! {
|
||||||
|
hostname(),
|
||||||
|
pools(),
|
||||||
|
cpu_time(),
|
||||||
|
memory(),
|
||||||
|
network_stats(),
|
||||||
|
temperatures(),
|
||||||
|
disk_stats(),
|
||||||
|
disk_usage(),
|
||||||
|
}?;
|
||||||
|
pin_mut!(network);
|
||||||
|
pin_mut!(disks);
|
||||||
|
pin_mut!(disk_usage);
|
||||||
|
let mut result = String::with_capacity(256);
|
||||||
|
writeln!(&mut result, "cpu_time{{host=\"{}\"}} {:.1}", hostname, cpu).ok();
|
||||||
|
writeln!(
|
||||||
|
&mut result,
|
||||||
|
"memory_total{{host=\"{}\"}} {}",
|
||||||
|
hostname, memory.total
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
writeln!(
|
||||||
|
&mut result,
|
||||||
|
"memory_available{{host=\"{}\"}} {}",
|
||||||
|
hostname, memory.available
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
writeln!(
|
||||||
|
&mut result,
|
||||||
|
"memory_free{{host=\"{}\"}} {}",
|
||||||
|
hostname, memory.free
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
for pool in pools {
|
||||||
|
writeln!(
|
||||||
|
&mut result,
|
||||||
|
"zfs_pool_size{{host=\"{}\", pool=\"{}\"}} {}",
|
||||||
|
hostname, pool.name, pool.size
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
writeln!(
|
||||||
|
&mut result,
|
||||||
|
"zfs_pool_free{{host=\"{}\", pool=\"{}\"}} {}",
|
||||||
|
hostname, pool.name, pool.free
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
while let Some(network) = network.next().await {
|
||||||
|
let network: IOStats = network;
|
||||||
|
if network.bytes_received > 0 || network.bytes_sent > 0 {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while let Some(disk) = disks.next().await {
|
||||||
|
let disk: IOStats = disk;
|
||||||
|
if disk.bytes_received > 0 && disk.bytes_sent > 0 {
|
||||||
|
writeln!(
|
||||||
|
&mut result,
|
||||||
|
"disk_sent{{host=\"{}\", disk=\"{}\"}} {}",
|
||||||
|
hostname, disk.interface, disk.bytes_sent
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
writeln!(
|
||||||
|
&mut result,
|
||||||
|
"disk_received{{host=\"{}\", disk=\"{}\"}} {}",
|
||||||
|
hostname, disk.interface, disk.bytes_received
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut found_sizes = HashSet::new();
|
||||||
|
while let Some(disk) = disk_usage.next().await {
|
||||||
|
let disk: DiskUsage = disk;
|
||||||
|
if disk.size > 0 {
|
||||||
|
if found_sizes.insert((disk.size, disk.free)) {
|
||||||
|
writeln!(
|
||||||
|
&mut result,
|
||||||
|
"disk_size{{host=\"{}\", disk=\"{}\"}} {}",
|
||||||
|
hostname, disk.name, disk.size
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
writeln!(
|
||||||
|
&mut result,
|
||||||
|
"disk_free{{host=\"{}\", disk=\"{}\"}} {}",
|
||||||
|
hostname, disk.name, disk.free
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (label, temp) in temperatures {
|
||||||
|
writeln!(
|
||||||
|
&mut result,
|
||||||
|
"temperature{{host=\"{}\", sensor=\"{}\"}} {:.1}",
|
||||||
|
hostname, label, temp
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
148
src/main.rs
148
src/main.rs
|
|
@ -1,13 +1,5 @@
|
||||||
pub mod heim;
|
|
||||||
mod zfs;
|
|
||||||
|
|
||||||
use crate::heim::{DiskUsage, Heim, IOStats, Memory, TemperatureLabel};
|
|
||||||
use crate::zfs::ZFS;
|
|
||||||
use color_eyre::{Report, Result};
|
use color_eyre::{Report, Result};
|
||||||
use futures_util::stream::StreamExt;
|
use palantir::get_metrics;
|
||||||
use futures_util::{pin_mut, try_join};
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use std::fmt::Write;
|
|
||||||
use warp::reject::Reject;
|
use warp::reject::Reject;
|
||||||
use warp::{Filter, Rejection};
|
use warp::{Filter, Rejection};
|
||||||
|
|
||||||
|
|
@ -23,131 +15,11 @@ impl From<Report> for ReportRejection {
|
||||||
|
|
||||||
impl Reject for ReportRejection {}
|
impl Reject for ReportRejection {}
|
||||||
|
|
||||||
async fn get_metrics(heim: Heim, zfs: ZFS) -> Result<String, ReportRejection> {
|
async fn serve_metrics() -> Result<String, Rejection> {
|
||||||
let (hostname, pools, cpu, memory, network, temperatures, disks, disk_usage): (
|
get_metrics()
|
||||||
String,
|
.await
|
||||||
Vec<DiskUsage>,
|
.map_err(ReportRejection::from)
|
||||||
f64,
|
.map_err(warp::reject::custom)
|
||||||
Memory,
|
|
||||||
_,
|
|
||||||
HashMap<TemperatureLabel, f32>,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
) = try_join! {
|
|
||||||
heim.hostname(),
|
|
||||||
zfs.pools(),
|
|
||||||
heim.cpu_time(),
|
|
||||||
heim.memory(),
|
|
||||||
heim.network_stats(),
|
|
||||||
heim.temperatures(),
|
|
||||||
heim.disk_stats(),
|
|
||||||
heim.disk_usage(),
|
|
||||||
}?;
|
|
||||||
pin_mut!(network);
|
|
||||||
pin_mut!(disks);
|
|
||||||
pin_mut!(disk_usage);
|
|
||||||
let mut result = String::with_capacity(256);
|
|
||||||
writeln!(&mut result, "cpu_time{{host=\"{}\"}} {:.1}", hostname, cpu).ok();
|
|
||||||
writeln!(
|
|
||||||
&mut result,
|
|
||||||
"memory_total{{host=\"{}\"}} {}",
|
|
||||||
hostname, memory.total
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
writeln!(
|
|
||||||
&mut result,
|
|
||||||
"memory_available{{host=\"{}\"}} {}",
|
|
||||||
hostname, memory.available
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
writeln!(
|
|
||||||
&mut result,
|
|
||||||
"memory_free{{host=\"{}\"}} {}",
|
|
||||||
hostname, memory.free
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
for pool in pools {
|
|
||||||
writeln!(
|
|
||||||
&mut result,
|
|
||||||
"zfs_pool_size{{host=\"{}\", pool=\"{}\"}} {}",
|
|
||||||
hostname, pool.name, pool.size
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
writeln!(
|
|
||||||
&mut result,
|
|
||||||
"zfs_pool_free{{host=\"{}\", pool=\"{}\"}} {}",
|
|
||||||
hostname, pool.name, pool.free
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
while let Some(network) = network.next().await {
|
|
||||||
let network: IOStats = network;
|
|
||||||
if network.bytes_received > 0 || network.bytes_sent > 0 {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while let Some(disk) = disks.next().await {
|
|
||||||
let disk: IOStats = disk;
|
|
||||||
if disk.bytes_received > 0 && disk.bytes_sent > 0 {
|
|
||||||
writeln!(
|
|
||||||
&mut result,
|
|
||||||
"disk_sent{{host=\"{}\", disk=\"{}\"}} {}",
|
|
||||||
hostname, disk.interface, disk.bytes_sent
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
writeln!(
|
|
||||||
&mut result,
|
|
||||||
"disk_received{{host=\"{}\", disk=\"{}\"}} {}",
|
|
||||||
hostname, disk.interface, disk.bytes_received
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut found_sizes = HashSet::new();
|
|
||||||
while let Some(disk) = disk_usage.next().await {
|
|
||||||
let disk: DiskUsage = disk;
|
|
||||||
if disk.size > 0 {
|
|
||||||
if found_sizes.insert((disk.size, disk.free)) {
|
|
||||||
writeln!(
|
|
||||||
&mut result,
|
|
||||||
"disk_size{{host=\"{}\", disk=\"{}\"}} {}",
|
|
||||||
hostname, disk.name, disk.size
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
writeln!(
|
|
||||||
&mut result,
|
|
||||||
"disk_free{{host=\"{}\", disk=\"{}\"}} {}",
|
|
||||||
hostname, disk.name, disk.free
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (label, temp) in temperatures {
|
|
||||||
writeln!(
|
|
||||||
&mut result,
|
|
||||||
"temperature{{host=\"{}\", sensor=\"{}\"}} {:.1}",
|
|
||||||
hostname, label, temp
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
Result::<_, ReportRejection>::Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn serve_metrics(heim: Heim, zfs: ZFS) -> Result<String, Rejection> {
|
|
||||||
get_metrics(heim, zfs).await.map_err(warp::reject::custom)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
|
@ -163,13 +35,7 @@ async fn main() -> Result<()> {
|
||||||
})
|
})
|
||||||
.expect("Error setting Ctrl-C handler");
|
.expect("Error setting Ctrl-C handler");
|
||||||
|
|
||||||
let heim = warp::any().map(|| Heim::default());
|
let metrics = warp::path!("metrics").and_then(serve_metrics);
|
||||||
let zfs = warp::any().map(|| ZFS::default());
|
|
||||||
|
|
||||||
let metrics = warp::path!("metrics")
|
|
||||||
.and(heim)
|
|
||||||
.and(zfs)
|
|
||||||
.and_then(serve_metrics);
|
|
||||||
|
|
||||||
warp::serve(metrics).run(([0, 0, 0, 0], host_port)).await;
|
warp::serve(metrics).run(([0, 0, 0, 0], host_port)).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,7 @@ use color_eyre::Result;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use tokio::task::spawn_blocking;
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
#[derive(Default)]
|
pub async fn pools() -> Result<Vec<DiskUsage>> {
|
||||||
pub struct ZFS;
|
|
||||||
|
|
||||||
impl ZFS {
|
|
||||||
pub async fn pools(self) -> Result<Vec<DiskUsage>> {
|
|
||||||
spawn_blocking(move || {
|
spawn_blocking(move || {
|
||||||
let mut z = Command::new("zpool");
|
let mut z = Command::new("zpool");
|
||||||
z.args(&["list", "-p", "-H", "-o", "name,size,free"]);
|
z.args(&["list", "-p", "-H", "-o", "name,size,free"]);
|
||||||
|
|
@ -33,4 +29,3 @@ impl ZFS {
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue