From d18a18a20d1f95a3b337b90776e1109c202111bb Mon Sep 17 00:00:00 2001 From: David Boucher Date: Sun, 29 Jun 2025 17:25:52 +0200 Subject: [PATCH] fix(generic-snmp): unwrap from main() removed and errors better handled --- experimental/src/generic/error.rs | 24 +++++++++----- experimental/src/generic/mod.rs | 44 ++++++++++++++++++++++++- experimental/src/main.rs | 55 ++++++++++++++++++------------- 3 files changed, 92 insertions(+), 31 deletions(-) diff --git a/experimental/src/generic/error.rs b/experimental/src/generic/error.rs index e3fdf22ad..c41e8d982 100644 --- a/experimental/src/generic/error.rs +++ b/experimental/src/generic/error.rs @@ -1,5 +1,5 @@ use snafu::prelude::Snafu; -use std::path::PathBuf; +use std::{io, path::PathBuf}; #[derive(Debug, Snafu)] #[snafu(visibility(pub))] @@ -21,14 +21,22 @@ pub enum Error { #[snafu(display("Threshold: The threshold syntax must follow '[@]start:end'"))] BadThreshold, - #[snafu(display("Json: Failed to parse JSON: {}", message))] - JsonParse { message: String }, + #[snafu(transparent)] + Io { source: io::Error }, + #[snafu(transparent)] + Lexopt { source: lexopt::Error }, - #[snafu(display("Json: Unable to read the JSON file '{}'", path.display()))] - JsonRead { - source: std::io::Error, - path: PathBuf, - }, + #[snafu(transparent)] + SerdeJson { source: serde_json::Error }, +} + +impl From for Error { + fn from(value: std::ffi::OsString) -> Self { + //let val = value.into_string().unwrap_or_else(|_| "Invalid UTF-8".to_string()); + Error::Lexopt { + source: lexopt::Error::NonUnicodeValue(value), + } + } } pub type Result = std::result::Result; diff --git a/experimental/src/generic/mod.rs b/experimental/src/generic/mod.rs index 9abe69f00..039da164c 100644 --- a/experimental/src/generic/mod.rs +++ b/experimental/src/generic/mod.rs @@ -1,3 +1,4 @@ +extern crate regex; extern crate serde; extern crate serde_json; @@ -8,6 +9,7 @@ use crate::compute::{Compute, Parser, ast::ExprResult, threshold::Threshold}; use crate::output::{Output, OutputFormatter}; use crate::snmp::{snmp_bulk_get, snmp_bulk_walk, snmp_bulk_walk_with_labels}; use log::{debug, trace}; +use regex::Regex; use serde::Deserialize; use std::collections::HashMap; @@ -207,13 +209,33 @@ impl Command { collect } - pub fn execute(&self, target: &str, version: &str, community: &str) -> Result { + pub fn execute( + &self, + target: &str, + version: &str, + community: &str, + filter_in: &Option, + filter_out: &Option, + ) -> Result { let mut collect = self.execute_snmp_collect(target, version, community); let mut idx: u32 = 0; let mut metrics = vec![]; let mut my_res = SnmpResult::new(HashMap::new()); let mut status = Status::Ok; + + // Prepare filters + let re_in = if let Some(filter_in) = filter_in { + Some(Regex::new(filter_in).unwrap()) + } else { + None + }; + let re_out: Option = if let Some(filter_out) = filter_out { + Some(Regex::new(filter_out).unwrap()) + } else { + None + }; + for metric in self.compute.metrics.iter() { let value = &metric.value; let parser = Parser::new(&collect); @@ -256,6 +278,16 @@ impl Command { panic!("A label must be a string"); } }; + if let Some(ref re) = re_in { + if !re.is_match(&name) { + continue; + } + } + if let Some(ref re) = re_out { + if re.is_match(&name) { + continue; + } + } let current_status = compute_status(item, &metric.warning, &metric.critical)?; status = worst(status, current_status); @@ -291,6 +323,16 @@ impl Command { res } }; + if let Some(ref re) = re_in { + if !re.is_match(&name) { + continue; + } + } + if let Some(ref re) = re_out { + if re.is_match(&name) { + continue; + } + } let current_status = compute_status(s, &metric.warning, &metric.critical)?; status = worst(status, current_status); let w = match metric.warning { diff --git a/experimental/src/main.rs b/experimental/src/main.rs index 5061abf5f..ab466ecd0 100644 --- a/experimental/src/main.rs +++ b/experimental/src/main.rs @@ -21,26 +21,18 @@ use generic::error::*; use lalrpop_util::lalrpop_mod; use lexopt::Arg; use log::trace; -use snafu::ResultExt; use std::fs; lalrpop_mod!(grammar); fn json_to_command(file_name: &str) -> Result { // Transform content of the file into a string - let contents = fs::read_to_string(file_name).context(JsonReadSnafu { path: file_name })?; - - let content = contents.as_str(); - - let module: serde_json::Result = serde_json::from_str(content); - match module { - Ok(c) => Ok(c), - Err(err) => Err(Error::JsonParse { - message: err.to_string(), - }), - } + let configuration = fs::read_to_string(file_name)?; + let command = serde_json::from_str(&configuration)?; + Ok(command) } +#[snafu::report] fn main() -> Result<(), Error> { env_logger::Builder::from_env( Env::default() @@ -57,6 +49,8 @@ fn main() -> Result<(), Error> { let mut port = 161; let mut snmp_version = "2c".to_string(); let mut snmp_community = "public".to_string(); + let mut filter_in = None; + let mut filter_out = None; let mut cmd: Option = None; loop { let arg = parser.next(); @@ -64,32 +58,43 @@ fn main() -> Result<(), Error> { Ok(arg) => match arg { Some(arg) => match arg { Short('H') | Long("hostname") => { - hostname = parser.value().unwrap().into_string().unwrap(); + hostname = parser.value()?.into_string()?; trace!("hostname: {:}", hostname); } Short('p') | Long("port") => { - port = parser.value().unwrap().parse::().unwrap(); + port = parser.value()?.parse::()?; trace!("port: {}", port); } Short('j') | Long("json") => { - let json = Some(parser.value().unwrap().into_string().unwrap()); - let json = json.unwrap(); + let json = parser.value()?.into_string()?; trace!("json: {:?}", json); cmd = Some(json_to_command(&json)?); } Short('v') | Long("snmp-version") => { - snmp_version = parser.value().unwrap().into_string().unwrap(); + snmp_version = parser.value()?.into_string()?; trace!("snmp_version: {}", snmp_version); } Short('c') | Long("snmp-community") => { - snmp_community = parser.value().unwrap().into_string().unwrap(); + snmp_community = parser.value()?.into_string()?; trace!("snmp_community: {}", snmp_community); } + Short('i') | Long("filter-in") => { + filter_in = Some(parser.value()?.into_string()?); + if let Some(ref filter) = filter_in { + trace!("filter_in: {}", filter); + } + } + Short('o') | Long("filter-out") => { + filter_out = Some(parser.value()?.into_string()?); + if let Some(ref filter) = filter_out { + trace!("filter_out: {}", filter); + } + } t => { if let Arg::Long(name) = t { if name.starts_with("warning-") { let wmetric = name[8..].to_string(); - let value = parser.value().unwrap().into_string().unwrap(); + let value = parser.value()?.into_string()?; match cmd.as_mut() { Some(ref mut cmd) => { if !value.is_empty() { @@ -105,7 +110,7 @@ fn main() -> Result<(), Error> { } } else if name.starts_with("critical-") { let cmetric = name[9..].to_string(); - let value = parser.value().unwrap().into_string().unwrap(); + let value = parser.value()?.into_string()?; match cmd.as_mut() { Some(ref mut cmd) => { if !value.is_empty() { @@ -136,7 +141,13 @@ fn main() -> Result<(), Error> { let url = format!("{}:{}", hostname, port); let result = match cmd { - Some(ref cmd) => cmd.execute(&url, &snmp_version, &snmp_community)?, + Some(ref cmd) => cmd.execute( + &url, + &snmp_version, + &snmp_community, + &filter_in, + &filter_out, + )?, None => { println!("json is empty"); std::process::exit(3); @@ -144,5 +155,5 @@ fn main() -> Result<(), Error> { }; println!("{}", result.output); - std::process::exit(result.status as i32); + Ok(()) }