enh(generic-snmp): Introduction of real thresholds (work in progress)

This commit is contained in:
David Boucher 2025-05-08 16:49:16 +02:00
parent 4fac604a4b
commit 32c1bbad3b
3 changed files with 154 additions and 2 deletions

View File

@ -1,5 +1,6 @@
pub mod ast;
pub mod lexer;
pub mod threshold;
use self::ast::ExprResult;
use self::lexer::{LexicalError, Tok};

View File

@ -0,0 +1,105 @@
use std::f64::INFINITY;
use log::{debug, error, info, trace, warn};
pub struct Threshold {
start: f64,
end: f64,
}
impl Threshold {
pub fn parse(expr: &str) -> Threshold {
// https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT
let mut start: usize = 0;
let mut in_number = false;
let mut start_value = -INFINITY;
let mut end_value = INFINITY;
let mut in_range = false;
for (idx, c) in expr.char_indices() {
if in_number {
match c {
'0'..='9' => continue,
'.' | '-' | '+' | 'e' | 'E' => continue,
_ => {
in_number = false;
start_value = match expr[start..idx].parse() {
Ok(x) => x,
Err(err) => {
error!("parse error: {}", err);
std::process::exit(3);
}
}
}
}
}
/* No else here, because we can continue the previous match */
if !in_number {
match c {
' ' => continue,
'0'..='9' => {
in_number = true;
start = idx;
}
':' => {
in_range = true;
}
_ => break,
}
}
}
if in_number {
start_value = match expr[start..].parse() {
Ok(x) => x,
Err(err) => {
error!("parse error: {}", err);
std::process::exit(3);
}
}
}
/* We have noticed a ':' character, so the threshold is a range */
if in_range {
return Threshold {
start: start_value,
end: INFINITY,
};
} else {
return Threshold {
start: 0_f64,
end: start_value,
};
}
}
fn in_alert(&self, value: f64) -> bool {
if value < self.start || value > self.end {
return true;
}
false
}
}
mod Test {
use super::*;
#[test]
fn test_parse_value() {
let expr = "1.2";
let threshold = Threshold::parse(expr);
assert_eq!(threshold.start, 0_f64);
assert_eq!(threshold.end, 1.2_f64);
assert!(threshold.in_alert(2_f64));
assert!(threshold.in_alert(-1_f64));
}
#[test]
fn test_parse_val_colon() {
let expr = "10:";
let threshold = Threshold::parse(expr);
assert_eq!(threshold.start, 10_f64);
assert_eq!(threshold.end, INFINITY);
assert!(!threshold.in_alert(10_f64));
assert!(!threshold.in_alert(11_f64));
assert!(threshold.in_alert(9_f64));
}
}

View File

@ -1,7 +1,7 @@
extern crate serde;
extern crate serde_json;
use compute::{ast::ExprResult, Compute, Parser};
use compute::{ast::ExprResult, threshold::Threshold, Compute, Parser};
use log::{debug, trace};
use serde::Deserialize;
use snmp::{snmp_bulk_get, snmp_bulk_walk, snmp_bulk_walk_with_labels};
@ -10,11 +10,13 @@ use std::{collections::HashMap, ops::IndexMut};
use crate::snmp::SnmpResult;
#[derive(Debug)]
struct Perfdata {
struct Perfdata<'p> {
name: String,
value: f64,
min: Option<f64>,
max: Option<f64>,
warning: Option<&'p str>,
critical: Option<&'p str>,
}
#[derive(Copy, Clone, PartialEq)]
@ -219,11 +221,22 @@ impl Command {
panic!("A label must be a string");
}
};
let status = compute_status(*item, &metric.warning, &metric.critical);
let w = match metric.warning {
Some(ref w) => Some(w.as_str()),
None => None,
};
let c = match metric.critical {
Some(ref c) => Some(c.as_str()),
None => None,
};
let m = Perfdata {
name,
value: *item,
min: compute_threshold(i, &min),
max: compute_threshold(i, &max),
warning: w,
critical: c,
};
trace!("New metric '{}' with value {:?}", m.name, m.value);
metrics.push(m);
@ -240,11 +253,22 @@ impl Command {
res
}
};
let status = compute_status(*s, &metric.warning, &metric.critical);
let w = match metric.warning {
Some(ref w) => Some(w.as_str()),
None => None,
};
let c = match metric.critical {
Some(ref c) => Some(c.as_str()),
None => None,
};
let m = Perfdata {
name,
value: *s,
min: compute_threshold(0, &min),
max: compute_threshold(0, &max),
warning: w,
critical: c,
};
trace!("New metric '{}' with value {:?}", m.name, m.value);
metrics.push(m);
@ -304,11 +328,22 @@ impl Command {
res
}
};
let status = compute_status(item, &metric.warning, &metric.critical);
let w = match metric.warning {
Some(ref w) => Some(w.as_str()),
None => None,
};
let c = match metric.critical {
Some(ref c) => Some(c.as_str()),
None => None,
};
let m = Perfdata {
name,
value: item,
min,
max,
warning: w,
critical: c,
};
trace!("New metric '{}' with value {:?}", m.name, m.value);
metrics.push(m);
@ -316,11 +351,22 @@ impl Command {
}
ExprResult::Number(s) => {
let name = &metric.name;
let status = compute_status(s, &metric.warning, &metric.critical);
let w = match metric.warning {
Some(ref w) => Some(w.as_str()),
None => None,
};
let c = match metric.critical {
Some(ref c) => Some(c.as_str()),
None => None,
};
let m = Perfdata {
name: name.to_string(),
value: s,
min,
max,
warning: w,
critical: c,
};
trace!("New metric '{}' with value {:?}", m.name, m.value);
metrics.push(m);