wip(generic-snmp)

This commit is contained in:
David Boucher 2025-04-29 14:36:18 +02:00
parent 6020f9973e
commit 9ff58afa42
6 changed files with 157 additions and 39 deletions

View File

@ -6,12 +6,13 @@ version = "1.0.0"
lalrpop = "0.22.1"
[dependencies]
regex = "1.11.1"
rasn = "0.26.2"
rasn-snmp = "0.26.2"
rasn-smi = "0.26.2"
log = "0.4.27"
env_logger = "0.11.8"
lalrpop-util = { version = "0.22.1", features = ["lexer"] }
lexopt = "0.3.1"
log = "0.4.27"
rasn = "0.26.2"
rasn-smi = "0.26.2"
rasn-snmp = "0.26.2"
regex = "1.11.1"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
lalrpop-util = { version = "0.22.1", features = ["lexer"] }

View File

@ -1,3 +1,4 @@
use log::{debug, info, trace};
use snmp::SnmpResult;
use std::str;
@ -232,20 +233,21 @@ impl<'input> Expr<'input> {
Expr::Number(n) => ExprResult::Scalar(*n),
Expr::Id(key) => {
let k = str::from_utf8(key).unwrap();
println!("Evaluation of Id '{}'", k);
for result in collect {
let item = &result.items[k];
match item {
ExprResult::Vector(n) => {
if n.len() == 1 {
println!("value {}", n[0]);
return ExprResult::Scalar(n[0]);
} else {
println!("value {:?}", n);
return ExprResult::Vector(n.clone());
match result.items.get(k) {
Some(item) => match item {
ExprResult::Vector(n) => {
if n.len() == 1 {
info!("ID '{}' has value {}", k, n[0]);
return ExprResult::Scalar(n[0]);
} else {
info!("ID '{}' has value {:?}", k, n);
return ExprResult::Vector(n.clone());
}
}
}
_ => panic!("Should be a number"),
_ => panic!("Should be a number"),
},
None => continue,
}
}
ExprResult::Scalar(0.0)

View File

@ -1,4 +1,4 @@
use log::debug;
use log::{debug, error, trace};
use std::str;
pub type Spanned<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
@ -54,7 +54,7 @@ impl<'input> Lexer<'input> {
end = chars.len();
}
debug!(
trace!(
"Token Number from {} to {} with value '{}'",
start,
end,
@ -83,7 +83,7 @@ impl<'input> Lexer<'input> {
if !done {
end = chars.len();
}
debug!(
trace!(
"Token Identifier from {} to {} with value '{}'",
start,
end,
@ -119,12 +119,12 @@ impl<'input> Iterator for Lexer<'input> {
}
b'(' => {
self.offset = i + 1;
debug!("Token LParen at {}", i);
trace!("Token LParen at {}", i);
return Some(Ok((i, Tok::LParen, i + 1)));
}
b')' => {
self.offset = i + 1;
debug!("Token RParen at {}", i);
trace!("Token RParen at {}", i);
return Some(Ok((i, Tok::RParen, i + 1)));
}
b'{' => {
@ -144,7 +144,7 @@ impl<'input> Iterator for Lexer<'input> {
}
_ => {
// Unknown character
debug!("Unknown character at {}: '{}'", i, *c as char);
error!("Unknown character at {}: '{}'", i, *c as char);
self.offset = i + 1;
return Some(Err(LexicalError::NotPossible));
}
@ -158,8 +158,13 @@ impl<'input> Iterator for Lexer<'input> {
mod Test {
use super::*;
fn init() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[test]
fn test_lexer_num_id_num() {
init();
let input = "123 abc 456";
let mut lexer = Lexer::new(input);
assert_eq!(lexer.next(), Some(Ok((0, Tok::Num(123_f64), 3))));
@ -170,6 +175,7 @@ mod Test {
#[test]
fn test_lexer_id_num_id() {
init();
let input = "abc 123 def";
let mut lexer = Lexer::new(input);
assert_eq!(lexer.next(), Some(Ok((0, Tok::Id(b"abc"), 3))));
@ -179,6 +185,7 @@ mod Test {
#[test]
fn test_lexer_num_op() {
init();
let input = "1+2*3";
let mut lexer = Lexer::new(input);
assert_eq!(lexer.next(), Some(Ok((0, Tok::Num(1_f64), 1))));

View File

@ -4,6 +4,8 @@ pub mod lexer;
use self::ast::ExprResult;
use self::lexer::{LexicalError, Tok};
use lalrpop_util::{lalrpop_mod, ParseError};
use log::{trace, debug};
use regex::Regex;
use serde::Deserialize;
use snmp::SnmpResult;
@ -44,6 +46,7 @@ impl<'a> Parser<'a> {
&self,
expr: &'a str,
) -> Result<ExprResult, ParseError<usize, Tok<'a>, LexicalError>> {
debug!("Parsing expression: {}", expr);
let lexer = lexer::Lexer::new(expr);
let res = self.parser.parse(lexer);
match res {
@ -54,14 +57,68 @@ impl<'a> Parser<'a> {
Err(e) => Err(e),
}
}
pub fn eval_str(
&self,
expr: &'a str,
) -> Result<ExprResult, ParseError<usize, Tok<'a>, LexicalError>> {
let re = Regex::new(r"\{[a-zA-Z_][a-zA-Z0-9_.]*\}").unwrap();
let mut suffix = expr;
let mut result: ExprResult = ExprResult::StrVector(vec![]);
loop {
let found = re.find(suffix);
if let Some(m) = found {
let start = m.start();
let end = m.end();
debug!(
"Identifier '{}' found in expr '{}'",
&expr[start + 1..end - 1],
expr
);
let prefix = &expr[0..start];
suffix = &expr[end..];
let mut result = vec![];
for snmp_result in self.collect {
if let Some(v) = snmp_result.items.get(&expr[start + 1..end - 1]) {
result = join_str_expr(prefix, v);
break;
}
}
trace!("Result string {:?}", result);
} else {
break;
}
}
Ok(result)
}
}
fn join_str_expr(prefix: &str, v: &ExprResult) -> Vec<String> {
match v {
ExprResult::StrVector(v) => {
let mut result = vec![];
for item in v {
result.push(format!("{}{}", prefix, item));
}
result
}
_ => panic!("Expected a string vector"),
}
}
mod Test {
use super::*;
use log::info;
use std::collections::HashMap;
fn init() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[test]
fn term() {
init();
info!("test term");
let lexer = lexer::Lexer::new("123");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
@ -80,6 +137,7 @@ mod Test {
#[test]
fn sum() {
init();
let lexer = lexer::Lexer::new("1 + 2");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
@ -129,6 +187,7 @@ mod Test {
#[test]
fn product() {
init();
let lexer = lexer::Lexer::new("2 * 3");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
@ -180,6 +239,7 @@ mod Test {
#[test]
fn sum_product() {
init();
let lexer = lexer::Lexer::new("1 + (3 + 2 * 3) / 3");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
@ -193,6 +253,7 @@ mod Test {
#[test]
fn identifier() {
init();
let lexer = lexer::Lexer::new("{abc} + 1");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
@ -212,6 +273,7 @@ mod Test {
#[test]
fn two_identifiers() {
init();
let lexer = lexer::Lexer::new("100 * (1 - {free}/{total})");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
@ -229,6 +291,7 @@ mod Test {
#[test]
fn function() {
init();
let lexer = lexer::Lexer::new("Average({abc})");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
@ -243,4 +306,34 @@ mod Test {
_ => panic!("Expected a scalar value"),
}
}
#[test]
fn identifier_str() {
init();
let items = HashMap::from([
(
"free".to_string(),
ExprResult::StrVector(vec!["free-one".to_string(), "free-two".to_string()]),
),
(
"total".to_string(),
ExprResult::StrVector(vec!["total-one".to_string(), "total-two".to_string()]),
),
]);
let collect = vec![SnmpResult::new(items)];
let parser = Parser::new(&collect);
let res = parser.eval_str("{free}foo{total}bar");
assert!(res.is_ok());
let res = res.unwrap();
match res {
ExprResult::StrVector(v) => {
assert_eq!(v.len(), 4);
assert_eq!(v[0], "free-onefoo");
assert_eq!(v[1], "free-twofoo");
assert_eq!(v[2], "total-onebar");
assert_eq!(v[3], "total-twobar");
}
_ => panic!("Expected a string vector"),
}
}
}

View File

@ -2,9 +2,12 @@ extern crate serde;
extern crate serde_json;
use compute::{ast::ExprResult, Compute, Parser};
use log::{debug, trace};
use serde::Deserialize;
use snmp::{snmp_bulk_get, snmp_bulk_walk, snmp_bulk_walk_with_labels};
use std::collections::HashMap;
use std::{collections::HashMap, ops::IndexMut};
use crate::snmp::SnmpResult;
#[derive(Debug)]
struct Perfdata {
@ -142,22 +145,23 @@ impl Command {
let r = snmp_bulk_get(target, version, community, 1, 1, &to_get, &get_name);
collect.push(r);
}
println!("{:#?}", collect);
let mut idx: u32 = 0;
let mut metrics = vec![];
let mut my_res = SnmpResult::new(HashMap::new());
for metric in self.compute.metrics.iter() {
let value = &metric.value;
let min = metric.min;
let max = metric.max;
let parser = Parser::new(&collect);
let value = parser.eval(value).unwrap();
println!("ExprResult: {:?}", value);
match value {
match &value {
ExprResult::Vector(v) => {
for item in v {
let name = match &metric.prefix {
Some(prefix) => {
let name_resolved = parser.eval_str(prefix).unwrap();
format!("{:?}#{}", prefix, metric.name)
}
None => {
@ -168,10 +172,11 @@ impl Command {
};
let m = Perfdata {
name,
value: item,
value: *item,
min,
max,
};
trace!("New metric '{}' with value {:?}", m.name, m.value);
metrics.push(m);
}
}
@ -188,16 +193,20 @@ impl Command {
};
let m = Perfdata {
name,
value: s,
value: *s,
min,
max,
};
trace!("New metric '{}' with value {:?}", m.name, m.value);
metrics.push(m);
}
_ => panic!("Aggregation must be applied to a vector"),
}
println!("perfdata: {:?}", metrics);
let key = format!("metrics.{}", metric.name);
debug!("New ID '{}' with content: {:?}", key, value);
my_res.items.insert(key, value);
}
collect.push(my_res);
if let Some(aggregations) = self.compute.aggregations.as_ref() {
for metric in aggregations {
let value = &metric.value;
@ -252,6 +261,7 @@ impl Command {
min,
max,
};
trace!("New metric '{}' with value {:?}", m.name, m.value);
metrics.push(m);
}
}
@ -263,14 +273,16 @@ impl Command {
min,
max,
};
trace!("New metric '{}' with value {:?}", m.name, m.value);
metrics.push(m);
}
_ => panic!("Aggregation must be applied to a vector"),
}
println!("perfdata: {:?}", metrics);
}
}
trace!("collect: {:#?}", collect);
println!("metrics: {:#?}", metrics);
CmdResult {
status: Status::Unknown,
output: "No result".to_string(),

View File

@ -1,3 +1,4 @@
extern crate env_logger;
extern crate lalrpop_util;
extern crate lexopt;
extern crate log;
@ -14,8 +15,8 @@ mod snmp;
use generic::Command;
use lalrpop_util::lalrpop_mod;
use log::{debug, info, trace};
use serde_json::Result;
//use snmp::snmp_get;
use std::fs;
lalrpop_mod!(grammar);
@ -67,6 +68,8 @@ fn json_to_command(file_name: &str) -> Result<Command> {
}
fn main() {
env_logger::init();
use lexopt::prelude::*;
let mut parser = lexopt::Parser::from_env();
let mut hostname = "localhost".to_string();
@ -78,30 +81,30 @@ fn main() {
let arg = parser.next();
match arg {
Ok(arg) => {
println!("{:?} ok", arg);
match arg {
Some(arg) => match arg {
Short('H') | Long("hostname") => {
hostname = parser.value().unwrap().into_string().unwrap();
trace!("hostname: {:}", hostname);
}
Short('p') | Long("port") => {
port = parser.value().unwrap().parse::<u16>().unwrap();
println!("port: {}", port);
trace!("port: {}", port);
}
Short('j') | Long("json") => {
json = Some(parser.value().unwrap().into_string().unwrap());
println!("json: {:?}", json);
trace!("json: {:?}", json);
}
Short('v') | Long("snmp-version") => {
snmp_version = parser.value().unwrap().into_string().unwrap();
println!("snmp_version: {}", snmp_version);
trace!("snmp_version: {}", snmp_version);
}
Short('c') | Long("snmp-community") => {
snmp_community = parser.value().unwrap().into_string().unwrap();
println!("snmp_community: {}", snmp_community);
trace!("snmp_community: {}", snmp_community);
}
_ => {
println!("other");
debug!("other unknown argument");
}
},
None => {