fix(generic-snmp): average fixed with NaN.

* Integration of criterion to make benches
* RUST_LOG env variable changed into PLUGIN_LOG
This commit is contained in:
David Boucher 2025-06-10 13:51:01 +02:00
parent 13ff270486
commit abbb15280c
4 changed files with 42 additions and 13 deletions

View File

@ -17,3 +17,10 @@ regex = "1.11.1"
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140" serde_json = "1.0.140"
snafu = "0.8.5" snafu = "0.8.5"
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
name = "bench"
harness = false

View File

@ -338,8 +338,19 @@ impl<'input> Expr<'input> {
Func::Average => match v { Func::Average => match v {
ExprResult::Number(n) => ExprResult::Number(n), ExprResult::Number(n) => ExprResult::Number(n),
ExprResult::Vector(v) => { ExprResult::Vector(v) => {
let sum = v.iter().sum::<f64>(); let mut sum = 0.0;
ExprResult::Number(sum / v.len() as f64) let mut count = 0;
for value in v {
if !value.is_nan() {
sum += value;
count += 1;
}
}
if count > 0 {
return ExprResult::Number(sum / count as f64);
} else {
return ExprResult::Number(f64::NAN);
}
} }
_ => panic!("Invalid operation"), _ => panic!("Invalid operation"),
}, },

View File

@ -99,16 +99,16 @@ pub struct CmdResult {
pub output: String, pub output: String,
} }
fn compute_status(value: f64, warn: &Option<String>, crit: &Option<String>) -> Result<Status> { fn compute_status(value: &f64, warn: &Option<String>, crit: &Option<String>) -> Result<Status> {
if let Some(c) = crit { if let Some(c) = crit {
let crit = Threshold::parse(c)?; let crit = Threshold::parse(c)?;
if crit.in_alert(value) { if crit.in_alert(*value) {
return Ok(Status::Critical); return Ok(Status::Critical);
} }
} }
if let Some(w) = warn { if let Some(w) = warn {
let warn = Threshold::parse(w)?; let warn = Threshold::parse(w)?;
if warn.in_alert(value) { if warn.in_alert(*value) {
return Ok(Status::Warning); return Ok(Status::Warning);
} }
} }
@ -247,7 +247,7 @@ impl Command {
} }
}; };
let current_status = let current_status =
compute_status(*item, &metric.warning, &metric.critical)?; compute_status(item, &metric.warning, &metric.critical)?;
status = worst(status, current_status); status = worst(status, current_status);
let w = match metric.warning { let w = match metric.warning {
Some(ref w) => Some(w.as_str()), Some(ref w) => Some(w.as_str()),
@ -280,7 +280,7 @@ impl Command {
res res
} }
}; };
let current_status = compute_status(*s, &metric.warning, &metric.critical)?; let current_status = compute_status(s, &metric.warning, &metric.critical)?;
status = worst(status, current_status); status = worst(status, current_status);
let w = match metric.warning { let w = match metric.warning {
Some(ref w) => Some(w.as_str()), Some(ref w) => Some(w.as_str()),
@ -309,6 +309,7 @@ impl Command {
} }
collect.push(my_res); collect.push(my_res);
if let Some(aggregations) = self.compute.aggregations.as_ref() { if let Some(aggregations) = self.compute.aggregations.as_ref() {
let mut my_res = SnmpResult::new(HashMap::new());
for metric in aggregations { for metric in aggregations {
let value = &metric.value; let value = &metric.value;
let parser = Parser::new(&collect); let parser = Parser::new(&collect);
@ -343,7 +344,7 @@ impl Command {
None None
}; };
let value = parser.eval(value).unwrap(); let value = parser.eval(value).unwrap();
match value { match &value {
ExprResult::Vector(v) => { ExprResult::Vector(v) => {
for item in v { for item in v {
let name = match &metric.prefix { let name = match &metric.prefix {
@ -369,7 +370,7 @@ impl Command {
}; };
let m = Perfdata { let m = Perfdata {
name, name,
value: item, value: *item,
min, min,
max, max,
warning: w, warning: w,
@ -393,7 +394,7 @@ impl Command {
}; };
let m = Perfdata { let m = Perfdata {
name: name.to_string(), name: name.to_string(),
value: s, value: *s,
min, min,
max, max,
warning: w, warning: w,
@ -404,12 +405,16 @@ impl Command {
} }
_ => panic!("Aggregation must be applied to a vector"), _ => panic!("Aggregation must be applied to a vector"),
} }
let key = format!("aggregations.{}", metric.name);
debug!("New ID '{}' with content: {:?}", key, value);
my_res.items.insert(key, value);
} }
collect.push(my_res);
} }
trace!("collect: {:#?}", collect); debug!("collect: {:#?}", collect);
trace!("metrics: {:#?}", metrics); trace!("metrics: {:#?}", metrics);
let output_formatter = OutputFormatter::new(status, &metrics, &self.output); let output_formatter = OutputFormatter::new(status, &collect, &metrics, &self.output);
let output = output_formatter.to_string(); let output = output_formatter.to_string();
Ok(CmdResult { status, output }) Ok(CmdResult { status, output })
} }

View File

@ -15,6 +15,7 @@ mod generic;
mod output; mod output;
mod snmp; mod snmp;
use env_logger::Env;
use generic::error::*; use generic::error::*;
use generic::Command; use generic::Command;
use lalrpop_util::lalrpop_mod; use lalrpop_util::lalrpop_mod;
@ -41,7 +42,12 @@ fn json_to_command(file_name: &str) -> Result<Command, Error> {
} }
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {
env_logger::init(); env_logger::Builder::from_env(
Env::default().default_filter_or("info")
.filter("PLUGIN_LOG")
).init();
//env_logger::init();
use lexopt::prelude::*; use lexopt::prelude::*;
let mut parser = lexopt::Parser::from_env(); let mut parser = lexopt::Parser::from_env();