enh(experimental): thresholds taken into acount

This commit is contained in:
David Boucher 2025-02-20 13:33:52 +01:00
parent a2c0e7530b
commit 0a21a2e8fa
2 changed files with 155 additions and 58 deletions

View File

@ -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<String>,
pub critical_core: Option<String>,
pub warning_agregation: Option<String>,
pub critical_agregation: Option<String>,
}
fn compute_status(value: f32, warn: &Option<String>, crit: &Option<String>) -> 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<String>, &'a Option<String>)>,
i32,
) {
let mut metrics: Vec<(String, f32, &Option<String>, &Option<String>)> = 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<f32> = Vec::new();
let mut labels: Vec<String> = 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<String>, &Option<String>)>,
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<String>,
values: &Vec<f32>,
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
}
}

View File

@ -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<String>,
#[arg(long, short = 'C')]
critical_core: Option<String>,
#[arg(long, short = 'a')]
warning_agregation: Option<String>,
#[arg(long, short = 'b')]
critical_agregation: Option<String>,
}
fn json_to_command(file_name: &str) -> Result<Command> {
// 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<Command> = 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);
}