From 0a21a2e8fa24e4217b5a8f779f18f4fb7060b1e3 Mon Sep 17 00:00:00 2001 From: David Boucher Date: Thu, 20 Feb 2025 13:33:52 +0100 Subject: [PATCH] enh(experimental): thresholds taken into acount --- experimental/src/generic/mod.rs | 179 +++++++++++++++++++++++--------- experimental/src/main.rs | 34 ++++-- 2 files changed, 155 insertions(+), 58 deletions(-) diff --git a/experimental/src/generic/mod.rs b/experimental/src/generic/mod.rs index 054588e17..3b5c9310d 100644 --- a/experimental/src/generic/mod.rs +++ b/experimental/src/generic/mod.rs @@ -3,7 +3,6 @@ extern crate serde_json; use lib::{r_snmp_bulk_walk, SnmpResult}; use serde::Deserialize; -use std::collections::{BTreeMap, HashMap}; #[derive(Deserialize, Debug, Clone, Copy)] enum Operation { @@ -60,8 +59,103 @@ pub struct CmdResult { pub output: String, } +pub struct CommandExt { + pub warning_core: Option, + pub critical_core: Option, + pub warning_agregation: Option, + pub critical_agregation: Option, +} + +fn compute_status(value: f32, warn: &Option, crit: &Option) -> i32 { + if let Some(c) = crit { + let crit = c.parse().unwrap(); + if value > crit { + return 2; + } + } + if let Some(w) = warn { + let warn = w.parse().unwrap(); + if value > warn { + return 1; + } + } + 0 +} + +fn build_status(metrics: &Vec<(String, f32, i32)>) -> i32 { + let mut retval = 0; + metrics.iter().for_each(|(_, _, s)| { + if *s > retval { + retval = *s; + if retval == 2 { + return; + } + } + }); + return retval; +} + +fn build_metrics<'a>( + values: &Vec<(String, f32)>, + ag: &Option<(&str, usize, f32)>, + ext: &'a CommandExt, +) -> ( + Vec<(String, f32, &'a Option, &'a Option)>, + i32, +) { + let mut metrics: Vec<(String, f32, &Option, &Option)> = Vec::new(); + let mut status = 0; + match ag { + Some(a) => { + // The agregation is located in first place + if a.1 == 0 { + let w = &ext.warning_agregation; + let c = &ext.critical_agregation; + let current_status = + compute_status(a.2, &ext.warning_agregation, &ext.critical_agregation); + metrics.push((a.0.to_string(), a.2, w, c)); + if current_status > status { + status = current_status; + } + } + } + None => (), + } + values.iter().enumerate().for_each(|(i, v)| { + let current_status = compute_status(v.1, &ext.warning_core, &ext.critical_core); + metrics.push(( + values[i].0.clone(), + v.1, + &ext.warning_core, + &ext.critical_core, + )); + if current_status > status { + status = current_status; + } + }); + match ag { + Some(a) => { + if a.1 > 0 { + let current_status = + compute_status(a.2, &ext.warning_agregation, &ext.critical_agregation); + metrics.push(( + a.0.to_string(), + a.2, + &ext.warning_agregation, + &ext.critical_agregation, + )); + if current_status > status { + status = current_status; + } + } + } + None => (), + } + (metrics, status) +} + impl Command { - pub fn execute(&self, target: &str) -> CmdResult { + pub fn execute(&self, target: &str, ext: &CommandExt) -> CmdResult { let mut agregation = ("", 0, Operation::None); let mut res: Option<(&str, SnmpResult)> = None; for (idx, entry) in self.leaf.entries.iter().enumerate() { @@ -78,26 +172,24 @@ impl Command { } match res { Some(r) => { - let mut values: Vec = Vec::new(); - let mut labels: Vec = Vec::new(); + let mut values: Vec<(String, f32)> = Vec::new(); let mut idx = 0; r.1.variables.iter().for_each(|v| { - values.push(v.value.parse().unwrap()); let label = r.0.replace("{idx}", &idx.to_string()); - labels.push(label); + values.push((label, v.value.parse().unwrap())); idx += 1; }); let count = values.len(); let ag = match agregation.2 { Operation::Average => { - let sum: f32 = values.iter().sum(); - Some((agregation.0, agregation.1, sum / values.len() as f32)) + let sum: f32 = values.iter().map(|(_, v)| v).sum(); + let avg = sum / values.len() as f32; + Some((agregation.0, agregation.1, avg)) } _ => None, }; - let metrics = self.build_metrics(&labels, &values, &ag); - let status = self.build_status(); - let output = self.build_output(count, status, &metrics, &ag); + let (metrics, status) = build_metrics(&values, &ag, &ext); + let output = self.build_output(count, status, &metrics, &ag, &ext); return CmdResult { status, output }; } None => { @@ -109,16 +201,13 @@ impl Command { } } - fn build_status(&self) -> i32 { - 0 - } - fn build_output( &self, count: usize, status: i32, - metrics: &Vec<(String, f32)>, + metrics: &Vec<(String, f32, &Option, &Option)>, ag: &Option<(&str, usize, f32)>, + ext: &CommandExt, ) -> String { let mut retval = self .leaf @@ -142,12 +231,20 @@ impl Command { retval += " |"; match &self.leaf.data { Some(d) => { - metrics.iter().for_each(|(k, v)| { + metrics.iter().for_each(|(k, v, w, c)| { retval += format!( - " {}={}{};;;{};{}", + " {}={}{};{};{};{};{}", k, v, d.uom, + match w { + Some(m) => m.to_string(), + None => "".to_string(), + }, + match c { + Some(m) => m.to_string(), + None => "".to_string(), + }, match d.min { Some(m) => m.to_string(), None => "".to_string(), @@ -161,40 +258,24 @@ impl Command { }); } None => { - metrics.iter().for_each(|(k, v)| { - retval += format!(" {}={}", k, v).as_str(); + metrics.iter().for_each(|(k, v, w, c)| { + retval += format!( + " {}={};{};{}", + k, + v, + match w { + Some(v) => v.to_string(), + None => "".to_string(), + }, + match c { + Some(v) => v.to_string(), + None => "".to_string(), + } + ) + .as_str(); }); } }; retval } - - fn build_metrics( - &self, - labels: &Vec, - values: &Vec, - ag: &Option<(&str, usize, f32)>, - ) -> Vec<(String, f32)> { - let mut metrics: Vec<(String, f32)> = Vec::new(); - match ag { - Some(a) => { - if a.1 == 0 { - metrics.push((a.0.to_string(), a.2)); - } - } - None => (), - } - values.iter().enumerate().for_each(|(i, v)| { - metrics.push((labels[i].clone(), *v)); - }); - match ag { - Some(a) => { - if a.1 > 0 { - metrics.push((a.0.to_string(), a.2)); - } - } - None => (), - } - metrics - } } diff --git a/experimental/src/main.rs b/experimental/src/main.rs index 1e3dc6efb..48dfa3422 100644 --- a/experimental/src/main.rs +++ b/experimental/src/main.rs @@ -7,26 +7,26 @@ extern crate regex; extern crate serde; extern crate serde_json; -mod lib; mod generic; +mod lib; use clap::Parser; +use generic::{Command, CommandExt}; use lib::r_snmp_get; use serde_json::Result; use std::fs; -use generic::{Command}; #[derive(Parser, Debug)] #[command(version, about)] struct Cli { /// Hostname to operate on - #[arg(long, short='H')] + #[arg(long, short = 'H')] hostname: String, #[arg(long, short, default_value_t = 161)] port: u16, - #[arg(long, short='v')] + #[arg(long, short = 'v')] snmp_version: String, #[arg(long, short)] @@ -34,15 +34,25 @@ struct Cli { #[arg(long, short)] json_conf: String, + + #[arg(long, short)] + warning_core: Option, + + #[arg(long, short = 'C')] + critical_core: Option, + + #[arg(long, short = 'a')] + warning_agregation: Option, + + #[arg(long, short = 'b')] + critical_agregation: Option, } fn json_to_command(file_name: &str) -> Result { - // Transform content of the file into a string - let contents = match fs::read_to_string(file_name) - { + let contents = match fs::read_to_string(file_name) { Ok(ret) => ret, - Err(err) => panic!("Could not deserialize the file, error code: {}", err) + Err(err) => panic!("Could not deserialize the file, error code: {}", err), }; let module: Result = serde_json::from_str(&contents.as_str()); @@ -54,7 +64,13 @@ fn main() { let url = format!("{}:{}", cli.hostname, cli.port); let cmd = json_to_command(&cli.json_conf); let cmd = cmd.unwrap(); - let result = cmd.execute(&url); + let ext = CommandExt { + warning_core: cli.warning_core, + critical_core: cli.critical_core, + warning_agregation: cli.warning_agregation, + critical_agregation: cli.critical_agregation, + }; + let result = cmd.execute(&url, &ext); println!("{}", result.output); std::process::exit(result.status); }