diff --git a/Cargo.lock b/Cargo.lock index 2dfc683..2f0b94d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "ansi_term" version = "0.11.0" @@ -445,27 +447,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "peshming" -version = "0.4.1" -dependencies = [ - "anyhow", - "async-anyhow-logger", - "chrono", - "clap", - "fern", - "futures", - "futures-util", - "hyper", - "lazy_static", - "log", - "prometheus", - "serde", - "tokio", - "tokio-icmp-echo", - "toml", -] - [[package]] name = "pin-project" version = "1.0.7" @@ -604,6 +585,27 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rexaping" +version = "0.4.1" +dependencies = [ + "anyhow", + "async-anyhow-logger", + "chrono", + "clap", + "fern", + "futures", + "futures-util", + "hyper", + "lazy_static", + "log", + "prometheus", + "serde", + "tokio", + "tokio-icmp-echo", + "toml", +] + [[package]] name = "scopeguard" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index e7111ab..fa22a7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "peshming" -version = "0.4.1" +name = "rexaping" +version = "0.5.0" license = "AGPL-3.0-only" -authors = ["Jan Christian Grünhage "] -repository = "https://git.jcg.re/jcgruenhage/peshming" +authors = ["Marek Isalski ", "Jan Christian Grünhage "] +repository = "https://gitea.faelix.net/FAELIX/rexaping" keywords = ["ping", "icmp", "prometheus"] -edition = "2018" -description = "Pings configured hosts in a configurable intervals and exposes metrics for prometheus." +edition = "2021" +description = "Pings configured hosts in a configurable intervals and labels, and exposes metrics for prometheus." [dependencies] prometheus = "0.12" diff --git a/README.md b/README.md index a5cda79..b5295bb 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,22 @@ -## peshming +## rexaping It's a prometheus exporter pinging hosts in the background. -It's been inspired by [meshping](https://bitbucket.org/Svedrin/meshping), -but instead of managing targets using a redis db this is using a simple config file. -In addition, this tool allows to set a ping frequency per target. +It's been forked from [peshming](https://git.jcg.re/jcgruenhage/peshming), +but only allowing targets to specify an interval, the config file can enrich +the Prometheus metrics with additional labels. -The name peshming is intended as a placeholder until -someone comes up with something better. +The name rexaping is a play on exaping, a tool with the same purpose +originally written in Python for Exa Networks. ### Usage: ``` -$ peshming --help -peshming 0.2.3 +$ rexaping --help +rexaping 0.2.3 +Marek Isalski Jan Christian Grünhage Pings configured hosts in a configurable intervals and exposes metrics for prometheus. USAGE: - peshming [FLAGS] + rexaping [FLAGS] FLAGS: -h, --help Prints help information diff --git a/config.toml.sample b/config.toml.sample index 25de410..0a91d8b 100644 --- a/config.toml.sample +++ b/config.toml.sample @@ -7,5 +7,5 @@ listener = "[::]:9898" # will ping the primary and secondary IP of cloudflare's 1.1.1.1 DNS service # every 500ms, or twice per second. [hosts] -"1.1.1.1" = 500 -"1.0.0.1" = 500 +"1.1.1.1" = { interval = "500", device = "one.one.one.one", interface="eth1111" } +"1.0.0.1" = { interval = "500", device = "one.one.one.one", interface="eth1001" } diff --git a/src/config.rs b/src/config.rs index f53b18e..9032a76 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,6 +3,7 @@ * * * Copyright (C) 2019-2020 Jan Christian Grünhage * * Copyright (C) 2020 Famedly GmbH * + * Copyright (C) 2021-2022 Faelix Limited * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -26,7 +27,7 @@ use std::collections::HashMap; #[derive(Serialize, Deserialize, Clone)] pub(crate) struct Config { pub(crate) listener: std::net::SocketAddr, - pub(crate) hosts: HashMap, + pub(crate) hosts: HashMap>, } fn setup_clap() -> clap::ArgMatches<'static> { diff --git a/src/main.rs b/src/main.rs index abd8244..f7220b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ * * * Copyright (C) 2019-2020 Jan Christian Grünhage * * Copyright (C) 2020 Famedly GmbH * + * Copyright (C) 2021-2022 Faelix Limited * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * diff --git a/src/metrics.rs b/src/metrics.rs index 4112f1f..99ee2c1 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -3,6 +3,7 @@ * * * Copyright (C) 2019-2020 Jan Christian Grünhage * * Copyright (C) 2020 Famedly GmbH * + * Copyright (C) 2021-2022 Faelix Limited * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * diff --git a/src/ping.rs b/src/ping.rs index e4894d8..7d59922 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -3,6 +3,7 @@ * * * Copyright (C) 2019-2020 Jan Christian Grünhage * * Copyright (C) 2020 Famedly GmbH * + * Copyright (C) 2021-2022 Faelix Limited * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -21,39 +22,54 @@ use crate::config::Config; use anyhow::{Context, Result}; use async_anyhow_logger::catch; use lazy_static::lazy_static; -use log::{info, trace}; +use log::{trace}; use prometheus::*; use std::net::IpAddr; use std::time::Duration; use tokio_icmp_echo::{PingFuture, Pinger}; +use std::collections::HashMap; lazy_static! { static ref PING_HISTOGRAM: HistogramVec = register_histogram_vec!( "ping_rtt_milliseconds", "The ping round trip time in milliseconds", - &["target"], + &["target", "device", "interface", "expected", "team", "priority"], vec![ - 0.5, 1.0, 5.0, 10.0, 15.0, 20.0, 25.0, 50.0, 75.0, 100.0, 150.0, 200.0, 250.0, 300.0, - 350.0, 400.0, 450.0, 500.0, 550.0, 600.0, 650.0, 700.0, 750.0, 800.0, 900.0, 1000.0, - 1250.0, 1500.0, 1750.0, 2000.0 + 0.125, 0.25, 0.5, 1.0, 1.5, 2.0, 2.5, 5.0, 7.5, 10.0, 15.0, 20.0, 25.0, 30.0, + 35.0, 40.0, 45.0, 50.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 90.0, 100.0, + 125.0, 150.0, 175.0, 200.0, 250.0, 300.0, 400.0, 500.0, 750.0, 1000.0, 2000.0, 4000.0 ] ) .unwrap(); } +lazy_static! { + static ref PING_COUNTER: IntCounterVec = + register_int_counter_vec!("ping_replies", "Number of ICMP ping responses received", &["target"]).unwrap(); +} + pub(crate) async fn start_pinging_hosts(config: &Config) -> Result<()> { let pinger = Pinger::new().await.context("Couldn't create pinger")?; let mut handles = vec![]; - for (host, interval) in config.hosts.clone() { - info!("Spawn ping task for {}", host); - handles.push(tokio::spawn(ping_host(pinger.clone(), host, interval))); + let mut interval; + + for (target, hostdata) in config.hosts.clone() { + let hd_interval = hostdata.get("interval"); + match hd_interval { + Some(ival) => { + interval = ival.parse()?; + }, + _ => interval = 1000, + } + + handles.push(tokio::spawn(ping_host(pinger.clone(), target, interval, hostdata))); } let (result, _, _) = futures::future::select_all(handles).await; result??; Ok(()) } -async fn ping_host(pinger: Pinger, host: IpAddr, interval: u64) -> Result<()> { +async fn ping_host(pinger: Pinger, host: IpAddr, interval: u64, hostdata: HashMap) -> Result<()> { let mut pingchain = pinger.chain(host).timeout(Duration::from_secs(3)); let mut interval = tokio::time::interval(Duration::from_millis(interval)); let host_string = host.to_string(); @@ -62,23 +78,41 @@ async fn ping_host(pinger: Pinger, host: IpAddr, interval: u64) -> Result<()> { tokio::spawn(catch(handle_ping_result( pingchain.send(), host_string.clone(), + hostdata.clone(), ))); } } -async fn handle_ping_result(result: PingFuture, host: String) -> Result<()> { +async fn handle_ping_result(result: PingFuture, host: String, hostdata: HashMap) -> Result<()> { let pong = result.await.context(format!("Couldn't ping {}", &host))?; + + let empty = "".to_string(); + let device = hostdata.get("device").unwrap_or(&empty); + let interface = hostdata.get("interface").unwrap_or(&empty); + let team = hostdata.get("team").unwrap_or(&empty); + + let up = "up".to_string(); + let expected = hostdata.get("expected").unwrap_or(&up); + + let notice = "notice".to_string(); + let severity = hostdata.get("severity").unwrap_or(¬ice); + match pong { Some(time) => { let ms = time.as_millis(); trace!("Received pong from {} after {} ms", &host, &ms); PING_HISTOGRAM - .with_label_values(&[&host]) + .with_label_values(&[&host, device, interface, expected, team, severity]) .observe(ms as f64); + //PING_COUNTER + // .with_label_values(&[&host]) + // .collect(); } None => { trace!("Received no response from {} within timeout", &host); - PING_HISTOGRAM.with_label_values(&[&host]).observe(3000.0); + PING_HISTOGRAM + .with_label_values(&[&host, device, interface, expected, team, severity]) + .observe(4000.0); } };