fix(generic-snmp): unwrap from main() removed and errors better handled

This commit is contained in:
David Boucher 2025-06-29 17:25:52 +02:00
parent 2736a097ce
commit d18a18a20d
3 changed files with 92 additions and 31 deletions

View File

@ -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<std::ffi::OsString> 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<T, E = Error> = std::result::Result<T, E>;

View File

@ -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<CmdResult> {
pub fn execute(
&self,
target: &str,
version: &str,
community: &str,
filter_in: &Option<String>,
filter_out: &Option<String>,
) -> Result<CmdResult> {
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<Regex> = 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 {

View File

@ -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<Command, Error> {
// 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<Command> = 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<Command> = 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::<u16>().unwrap();
port = parser.value()?.parse::<u16>()?;
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(())
}