enh(generic-snmp): bugs fixed and unit tests added

This commit is contained in:
David Boucher 2025-05-04 19:07:57 +02:00
parent 9ff58afa42
commit bc64e1e2ae
7 changed files with 613 additions and 156 deletions

View File

@ -1,34 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
use lib '/home/david/perl5/lib/perl5';
use FFI::Platypus 2.00;
my $ffi = FFI::Platypus->new(api => 2, lang => => 'Rust');
$ffi->lib(
'../target/debug/libconn.so');
### Types ###
$ffi->type('object(SnmpResult)' => 'snmpresult_t');
$ffi->type('object(SnmpVariable)' => 'snmpvariable_t');
### Global functions ###
$ffi->attach(snmp_get => ['string', 'string', 'string'] => 'snmpresult_t');
$ffi->attach(snmp_walk => ['string', 'string'] => 'snmpresult_t');
$ffi->attach(snmpresult_variables_count => ['snmpresult_t'] => 'usize');
$ffi->attach(snmpresult_get_variable => ['snmpresult_t', 'usize'] => 'snmpvariable_t');
$ffi->attach( snmpresult_DESTROY => [ 'snmpresult_t' ] );
$ffi->attach(snmpvariable_get_name => ['snmpvariable_t'] => 'string');
$ffi->attach(snmpvariable_get_value => ['snmpvariable_t'] => 'string');
$ffi->attach( snmpvariable_DESTROY => [ 'snmpvariable_t' ] );
### Main program ###
my $result = snmp_walk('127.0.0.1:161', '1.3.6.1.2.1.25.3.3.1.2');
for (my $i = 0; $i < snmpresult_variables_count($result); $i++) {
my $variable = snmpresult_get_variable($result, $i);
print snmpvariable_get_name($variable) . " => " . snmpvariable_get_value($variable) . "\n";
}

View File

@ -1,4 +1,4 @@
use log::{debug, info, trace};
use log::{debug, info, trace, warn};
use snmp::SnmpResult;
use std::str;
@ -23,8 +23,10 @@ pub enum Func {
#[derive(Debug)]
pub enum ExprResult {
Vector(Vec<f64>),
Scalar(f64),
Number(f64),
StrVector(Vec<String>),
Str(String),
Empty,
}
impl std::ops::Add for ExprResult {
@ -32,7 +34,7 @@ impl std::ops::Add for ExprResult {
fn add(self, other: Self) -> Self::Output {
match (self, other) {
(ExprResult::Scalar(a), ExprResult::Scalar(b)) => ExprResult::Scalar(a + b),
(ExprResult::Number(a), ExprResult::Number(b)) => ExprResult::Number(a + b),
(ExprResult::Vector(a), ExprResult::Vector(b)) => {
let len_a = a.len();
let len_b = b.len();
@ -43,14 +45,18 @@ impl std::ops::Add for ExprResult {
}
ExprResult::Vector(result)
} else {
warn!(
"Trying to add to arrays of different lengths: {} and {}",
len_a, len_b
);
if len_a > len_b {
let mut result = a.clone();
let mut result = a;
for (idx, value) in b.iter().enumerate() {
result[idx] += value;
}
ExprResult::Vector(result)
} else {
let mut result = b.clone();
let mut result = b;
for (idx, value) in a.iter().enumerate() {
result[idx] += value;
}
@ -58,15 +64,15 @@ impl std::ops::Add for ExprResult {
}
}
}
(ExprResult::Scalar(a), ExprResult::Vector(b)) => {
let mut result = b.clone();
(ExprResult::Number(a), ExprResult::Vector(b)) => {
let mut result = b;
for value in result.iter_mut() {
*value += a;
}
ExprResult::Vector(result)
}
(ExprResult::Vector(a), ExprResult::Scalar(b)) => {
let mut result = a.clone();
(ExprResult::Vector(a), ExprResult::Number(b)) => {
let mut result = a;
for value in result.iter_mut() {
*value += b;
}
@ -82,7 +88,7 @@ impl std::ops::Sub for ExprResult {
fn sub(self, other: Self) -> Self::Output {
match (self, other) {
(ExprResult::Scalar(a), ExprResult::Scalar(b)) => ExprResult::Scalar(a - b),
(ExprResult::Number(a), ExprResult::Number(b)) => ExprResult::Number(a - b),
(ExprResult::Vector(a), ExprResult::Vector(b)) => {
let len_a = a.len();
let len_b = b.len();
@ -93,30 +99,38 @@ impl std::ops::Sub for ExprResult {
}
ExprResult::Vector(result)
} else {
warn!(
"Trying to subtract arrays of different lengths: {} and {}",
len_a, len_b
);
if len_a > len_b {
let mut result = a.clone();
let mut result = a;
for (idx, value) in b.iter().enumerate() {
result[idx] -= value;
}
ExprResult::Vector(result)
} else {
let mut result = b.clone();
for (idx, value) in a.iter().enumerate() {
result[idx] -= value;
let mut result = b;
for (idx, value) in result.iter_mut().enumerate() {
if idx < a.len() {
*value = a[idx] - *value;
} else {
*value = -*value;
}
}
ExprResult::Vector(result)
}
}
}
(ExprResult::Scalar(a), ExprResult::Vector(b)) => {
(ExprResult::Number(a), ExprResult::Vector(b)) => {
let mut result = vec![0_f64; b.len()];
for (idx, value) in result.iter_mut().enumerate() {
*value = a - b[idx];
}
ExprResult::Vector(result)
}
(ExprResult::Vector(a), ExprResult::Scalar(b)) => {
let mut result = a.clone();
(ExprResult::Vector(a), ExprResult::Number(b)) => {
let mut result = a;
for value in result.iter_mut() {
*value -= b;
}
@ -132,7 +146,7 @@ impl std::ops::Mul for ExprResult {
fn mul(self, other: Self) -> Self::Output {
match (self, other) {
(ExprResult::Scalar(a), ExprResult::Scalar(b)) => ExprResult::Scalar(a * b),
(ExprResult::Number(a), ExprResult::Number(b)) => ExprResult::Number(a * b),
(ExprResult::Vector(a), ExprResult::Vector(b)) => {
let len_a = a.len();
let len_b = b.len();
@ -143,14 +157,18 @@ impl std::ops::Mul for ExprResult {
}
ExprResult::Vector(result)
} else {
warn!(
"Trying to multiply arrays of different lengths: {} and {}",
len_a, len_b
);
if len_a > len_b {
let mut result = a.clone();
let mut result = a;
for (idx, value) in b.iter().enumerate() {
result[idx] *= value;
}
ExprResult::Vector(result)
} else {
let mut result = b.clone();
let mut result = b;
for (idx, value) in a.iter().enumerate() {
result[idx] *= value;
}
@ -158,14 +176,14 @@ impl std::ops::Mul for ExprResult {
}
}
}
(ExprResult::Scalar(a), ExprResult::Vector(b)) => {
(ExprResult::Number(a), ExprResult::Vector(b)) => {
let mut result = b.clone();
for value in result.iter_mut() {
*value *= a;
}
ExprResult::Vector(result)
}
(ExprResult::Vector(a), ExprResult::Scalar(b)) => {
(ExprResult::Vector(a), ExprResult::Number(b)) => {
let mut result = a.clone();
for value in result.iter_mut() {
*value *= b;
@ -182,7 +200,7 @@ impl std::ops::Div for ExprResult {
fn div(self, other: Self) -> Self::Output {
match (self, other) {
(ExprResult::Scalar(a), ExprResult::Scalar(b)) => ExprResult::Scalar(a / b),
(ExprResult::Number(a), ExprResult::Number(b)) => ExprResult::Number(a / b),
(ExprResult::Vector(a), ExprResult::Vector(b)) => {
let len_a = a.len();
let len_b = b.len();
@ -193,30 +211,38 @@ impl std::ops::Div for ExprResult {
}
ExprResult::Vector(result)
} else {
warn!(
"Trying to divide arrays of different lengths: {} and {}",
len_a, len_b
);
if len_a > len_b {
let mut result = a.clone();
let mut result = a;
for (idx, value) in b.iter().enumerate() {
result[idx] /= value;
}
ExprResult::Vector(result)
} else {
let mut result = b.clone();
for (idx, value) in a.iter().enumerate() {
result[idx] /= value;
let mut result = b;
for (idx, value) in result.iter_mut().enumerate() {
if idx < a.len() {
*value = a[idx] / *value;
} else {
*value = 1_f64 / *value;
}
}
ExprResult::Vector(result)
}
}
}
(ExprResult::Scalar(a), ExprResult::Vector(b)) => {
(ExprResult::Number(a), ExprResult::Vector(b)) => {
let mut result = vec![0_f64; b.len()];
for (idx, value) in result.iter_mut().enumerate() {
*value = a / b[idx];
}
ExprResult::Vector(result)
}
(ExprResult::Vector(a), ExprResult::Scalar(b)) => {
let mut result = a.clone();
(ExprResult::Vector(a), ExprResult::Number(b)) => {
let mut result = a;
for value in result.iter_mut() {
*value /= b;
}
@ -227,10 +253,60 @@ impl std::ops::Div for ExprResult {
}
}
impl ExprResult {
pub fn join(&mut self, other: &ExprResult) {
match self {
ExprResult::Empty => match other {
ExprResult::StrVector(vv) => {
*self = ExprResult::StrVector(vv.clone());
}
ExprResult::Str(s) => {
*self = ExprResult::Str(s.clone());
}
_ => panic!("Unable to join objects others than strings"),
},
ExprResult::StrVector(v) => match other {
ExprResult::StrVector(vv) => {
if v.len() != vv.len() {
warn!(
"Trying to join arrays of different lengths: {} and {}",
v.len(),
vv.len()
);
if v.len() < vv.len() {
v.resize(vv.len(), "".to_string());
}
}
for (key, value) in v.iter_mut().enumerate() {
value.push_str(vv.get(key).unwrap_or(&"".to_string()));
}
}
ExprResult::Str(s) => {
*v = v.iter().map(|a| format!("{}{}", a, s)).collect();
}
_ => panic!("Unable to join objects others than strings"),
},
ExprResult::Str(s) => match other {
ExprResult::StrVector(vv) => {
*self =
ExprResult::StrVector(vv.iter().map(|a| format!("{}{}", s, a)).collect());
}
ExprResult::Str(ss) => {
*s = format!("{}{}", s, ss);
}
_ => panic!("Unable to join objects others than strings"),
},
_ => {
panic!("Unable to join objects that are not strings");
}
}
}
}
impl<'input> Expr<'input> {
pub fn eval(&self, collect: &Vec<SnmpResult>) -> ExprResult {
match self {
Expr::Number(n) => ExprResult::Scalar(*n),
Expr::Number(n) => ExprResult::Number(*n),
Expr::Id(key) => {
let k = str::from_utf8(key).unwrap();
for result in collect {
@ -239,7 +315,7 @@ impl<'input> Expr<'input> {
ExprResult::Vector(n) => {
if n.len() == 1 {
info!("ID '{}' has value {}", k, n[0]);
return ExprResult::Scalar(n[0]);
return ExprResult::Number(n[0]);
} else {
info!("ID '{}' has value {:?}", k, n);
return ExprResult::Vector(n.clone());
@ -250,7 +326,7 @@ impl<'input> Expr<'input> {
None => continue,
}
}
ExprResult::Scalar(0.0)
ExprResult::Number(0.0)
}
Expr::OpPlus(left, right) => left.eval(collect) + right.eval(collect),
Expr::OpMinus(left, right) => left.eval(collect) - right.eval(collect),
@ -260,26 +336,26 @@ impl<'input> Expr<'input> {
let v = expr.eval(collect);
match func {
Func::Average => match v {
ExprResult::Scalar(n) => ExprResult::Scalar(n),
ExprResult::Number(n) => ExprResult::Number(n),
ExprResult::Vector(v) => {
let sum = v.iter().sum::<f64>();
ExprResult::Scalar(sum / v.len() as f64)
ExprResult::Number(sum / v.len() as f64)
}
_ => panic!("Invalid operation"),
},
Func::Min => match v {
ExprResult::Scalar(n) => ExprResult::Scalar(n),
ExprResult::Number(n) => ExprResult::Number(n),
ExprResult::Vector(v) => {
let min = v.iter().cloned().fold(f64::INFINITY, f64::min);
ExprResult::Scalar(min)
ExprResult::Number(min)
}
_ => panic!("Invalid operation"),
},
Func::Max => match v {
ExprResult::Scalar(n) => ExprResult::Scalar(n),
ExprResult::Number(n) => ExprResult::Number(n),
ExprResult::Vector(v) => {
let max = v.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
ExprResult::Scalar(max)
ExprResult::Number(max)
}
_ => panic!("Invalid operation"),
},

View File

@ -186,13 +186,13 @@ mod Test {
#[test]
fn test_lexer_num_op() {
init();
let input = "1+2*3";
let input = "1 +2*3";
let mut lexer = Lexer::new(input);
assert_eq!(lexer.next(), Some(Ok((0, Tok::Num(1_f64), 1))));
assert_eq!(lexer.next(), Some(Ok((1, Tok::OpPlus, 2))));
assert_eq!(lexer.next(), Some(Ok((2, Tok::Num(2_f64), 3))));
assert_eq!(lexer.next(), Some(Ok((3, Tok::OpStar, 4))));
assert_eq!(lexer.next(), Some(Ok((4, Tok::Num(3_f64), 5))));
assert_eq!(lexer.next(), Some(Ok((2, Tok::OpPlus, 3))));
assert_eq!(lexer.next(), Some(Ok((3, Tok::Num(2_f64), 4))));
assert_eq!(lexer.next(), Some(Ok((4, Tok::OpStar, 5))));
assert_eq!(lexer.next(), Some(Ok((5, Tok::Num(3_f64), 6))));
assert_eq!(lexer.next(), None);
}
}

View File

@ -4,7 +4,7 @@ pub mod lexer;
use self::ast::ExprResult;
use self::lexer::{LexicalError, Tok};
use lalrpop_util::{lalrpop_mod, ParseError};
use log::{trace, debug};
use log::{debug, trace};
use regex::Regex;
use serde::Deserialize;
use snmp::SnmpResult;
@ -64,28 +64,28 @@ impl<'a> Parser<'a> {
) -> 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![]);
let mut result: ExprResult = ExprResult::Empty;
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![];
if start > 0 {
result.join(&ExprResult::Str(suffix[0..start].to_string()));
}
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);
if let Some(v) = snmp_result.items.get(&suffix[start + 1..end - 1]) {
result.join(v);
break;
}
}
trace!("Result string {:?}", result);
debug!(
"Evaluation as string of expression '{}' returns {:?}",
suffix, result
);
suffix = &suffix[end..];
} else {
result.join(&ExprResult::Str(suffix.to_string()));
break;
}
}
@ -125,7 +125,7 @@ mod Test {
let snmp_result = vec![];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 123_f64),
ExprResult::Number(n) => assert!(n == 123_f64),
_ => panic!("Expected a scalar value"),
}
let lexer = lexer::Lexer::new("123");
@ -144,7 +144,7 @@ mod Test {
let snmp_result = vec![];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 3_f64),
ExprResult::Number(n) => assert!(n == 3_f64),
_ => panic!("Expected a scalar value"),
}
@ -153,7 +153,7 @@ mod Test {
assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 0_f64),
ExprResult::Number(n) => assert!(n == 0_f64),
_ => panic!("Expected a scalar value"),
}
@ -162,7 +162,7 @@ mod Test {
assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 2_f64),
ExprResult::Number(n) => assert!(n == 2_f64),
_ => panic!("Expected a scalar value"),
}
@ -171,7 +171,7 @@ mod Test {
assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == -4_f64),
ExprResult::Number(n) => assert!(n == -4_f64),
_ => panic!("Expected a scalar value"),
}
@ -180,7 +180,7 @@ mod Test {
assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == -8_f64),
ExprResult::Number(n) => assert!(n == -8_f64),
_ => panic!("Expected a scalar value"),
}
}
@ -194,7 +194,7 @@ mod Test {
let snmp_result = vec![];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 6_f64),
ExprResult::Number(n) => assert!(n == 6_f64),
_ => panic!("Expected a scalar value"),
}
@ -203,7 +203,7 @@ mod Test {
assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 7_f64),
ExprResult::Number(n) => assert!(n == 7_f64),
_ => panic!("Expected a scalar value"),
}
@ -212,7 +212,7 @@ mod Test {
assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 9_f64),
ExprResult::Number(n) => assert!(n == 9_f64),
_ => panic!("Expected a scalar value"),
}
@ -221,7 +221,7 @@ mod Test {
assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 24_f64),
ExprResult::Number(n) => assert!(n == 24_f64),
_ => panic!("Expected a scalar value"),
}
@ -230,7 +230,7 @@ mod Test {
assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 3_f64),
ExprResult::Number(n) => assert!(n == 3_f64),
_ => panic!("Expected a scalar value"),
}
@ -246,7 +246,7 @@ mod Test {
let snmp_result = vec![];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 4_f64),
ExprResult::Number(n) => assert!(n == 4_f64),
_ => panic!("Expected a scalar value"),
}
}
@ -262,7 +262,7 @@ mod Test {
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 2_f64),
ExprResult::Number(n) => assert!(n == 2_f64),
_ => panic!("Expected a scalar value"),
}
@ -271,6 +271,370 @@ mod Test {
assert!(res.is_err());
}
#[test]
fn vec_sum() {
init();
let lexer = lexer::Lexer::new("{abc} + {def}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
println!("{:?}", res);
let items = HashMap::from([
("abc".to_string(), ExprResult::Vector(vec![1_f64, 2_f64])),
("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])),
(
"ghi".to_string(),
ExprResult::Vector(vec![5_f64, 6_f64, 7_f64]),
),
]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Vector(v) => assert!(v == vec![4_f64, 6_f64]),
_ => panic!("Expected a vector value"),
}
let lexer = lexer::Lexer::new("abc + 1");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_err());
}
#[test]
fn vec_sum_diff_len() {
let lexer = lexer::Lexer::new("{abc} + {def} + {ghi}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([
(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
),
("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])),
(
"ghi".to_string(),
ExprResult::Vector(vec![5_f64, 6_f64, 7_f64, 8_f64]),
),
]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![9_f64, 12_f64, 12_f64, 8_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn vec_scalar_sum() {
let lexer = lexer::Lexer::new("{abc} + 5");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
)]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![6_f64, 7_f64, 10_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn scalar_vec_sum() {
let lexer = lexer::Lexer::new("18 + {abc}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
)]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![19_f64, 20_f64, 23_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn vec_sub() {
init();
let lexer = lexer::Lexer::new("{abc} - {def}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
println!("{:?}", res);
let items = HashMap::from([
("abc".to_string(), ExprResult::Vector(vec![1_f64, 3_f64])),
("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])),
(
"ghi".to_string(),
ExprResult::Vector(vec![5_f64, 6_f64, 7_f64]),
),
]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Vector(v) => assert!(v == vec![-2_f64, -1_f64]),
_ => panic!("Expected a vector value"),
}
let lexer = lexer::Lexer::new("abc + 1");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_err());
}
#[test]
fn vec_sub_diff_len() {
let lexer = lexer::Lexer::new("{abc} - {def} - {ghi}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([
(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
),
("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])),
(
"ghi".to_string(),
ExprResult::Vector(vec![5_f64, 6_f64, 7_f64, 8_f64]),
),
]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![-7_f64, -8_f64, -2_f64, -8_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn scalar_vec_sub() {
let lexer = lexer::Lexer::new("18 - {abc}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
)]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![17_f64, 16_f64, 13_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn vec_scalar_sub() {
let lexer = lexer::Lexer::new("{abc} - 19.2");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
)]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![-18.2_f64, -17.2_f64, -14.2_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn vec_mul() {
init();
let lexer = lexer::Lexer::new("{abc} * {def}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
println!("{:?}", res);
let items = HashMap::from([
("abc".to_string(), ExprResult::Vector(vec![1_f64, 3_f64])),
("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])),
(
"ghi".to_string(),
ExprResult::Vector(vec![5_f64, 6_f64, 7_f64]),
),
]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Vector(v) => assert!(v == vec![3_f64, 12_f64]),
_ => panic!("Expected a vector value"),
}
let lexer = lexer::Lexer::new("abc * 1");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_err());
}
#[test]
fn vec_mul_diff_len() {
let lexer = lexer::Lexer::new("{abc} * {def} * {ghi}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([
(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
),
("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])),
(
"ghi".to_string(),
ExprResult::Vector(vec![5_f64, 6_f64, 7_f64, 8_f64]),
),
]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![15_f64, 48_f64, 35_f64, 8_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn scalar_vec_mul() {
let lexer = lexer::Lexer::new("18 * {abc}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
)]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![18_f64, 36_f64, 90_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn vec_scalar_mul() {
let lexer = lexer::Lexer::new("{abc} * 4");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
)]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![4_f64, 8_f64, 20_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn vec_div() {
init();
let lexer = lexer::Lexer::new("{abc} / {def}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
println!("{:?}", res);
let items = HashMap::from([
("abc".to_string(), ExprResult::Vector(vec![3_f64, 12_f64])),
("def".to_string(), ExprResult::Vector(vec![1_f64, 3_f64])),
(
"ghi".to_string(),
ExprResult::Vector(vec![5_f64, 6_f64, 7_f64]),
),
]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Vector(v) => assert!(v == vec![3_f64, 4_f64]),
_ => panic!("Expected a vector value"),
}
let lexer = lexer::Lexer::new("abc * 1");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_err());
}
#[test]
fn vec_div_diff_len() {
let lexer = lexer::Lexer::new("{abc} / {def} / {ghi}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([
(
"abc".to_string(),
ExprResult::Vector(vec![12_f64, 22_f64, 15_f64]),
),
("def".to_string(), ExprResult::Vector(vec![3_f64, 11_f64])),
(
"ghi".to_string(),
ExprResult::Vector(vec![2_f64, 2_f64, 3_f64, 8_f64]),
),
]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![2_f64, 1_f64, 5_f64, 0.125_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn scalar_vec_div() {
let lexer = lexer::Lexer::new("18 / {abc}");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
)]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![18_f64, 9_f64, 3.6_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn vec_scalar_div() {
let lexer = lexer::Lexer::new("{abc} / 4");
let res = grammar::ExprParser::new().parse(lexer);
assert!(res.is_ok());
debug!("{:?}", res);
let items = HashMap::from([(
"abc".to_string(),
ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]),
)]);
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
debug!("{:?}", res);
match res {
ExprResult::Vector(v) => assert!(v == vec![0.25_f64, 0.5_f64, 1.25_f64]),
_ => panic!("Expected a vector value"),
}
}
#[test]
fn two_identifiers() {
init();
@ -284,7 +648,7 @@ mod Test {
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 96.04125652657707_f64),
ExprResult::Number(n) => assert!(n == 96.04125652657707_f64),
_ => panic!("Expected a scalar value"),
}
}
@ -302,13 +666,13 @@ mod Test {
let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result);
match res {
ExprResult::Scalar(n) => assert!(n == 2_f64),
ExprResult::Number(n) => assert!(n == 2_f64),
_ => panic!("Expected a scalar value"),
}
}
#[test]
fn identifier_str() {
fn join_str_str_identifier() {
init();
let items = HashMap::from([
(
@ -327,13 +691,50 @@ mod Test {
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");
assert_eq!(v.len(), 2);
assert_eq!(v[0], "free-onefoototal-onebar");
assert_eq!(v[1], "free-twofoototal-twobar");
}
_ => panic!("Expected a string vector"),
}
}
#[test]
fn join_str_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("test{free}{total}foo{free}");
assert!(res.is_ok());
let res = res.unwrap();
match res {
ExprResult::StrVector(v) => {
assert_eq!(v.len(), 2);
assert_eq!(v[0], "testfree-onetotal-onefoofree-one");
assert_eq!(v[1], "testfree-twototal-twofoofree-two");
}
_ => panic!("Expected a string vector"),
}
}
#[test]
fn join_str_str_str() {
let mut a = ExprResult::Str("test".to_string());
let b = ExprResult::Str("foobar".to_string());
a.join(&b);
match a {
ExprResult::Str(s) => assert_eq!(s, "testfoobar".to_string()),
_ => panic!("Expected a string"),
}
}
}

View File

@ -158,17 +158,21 @@ impl Command {
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 => {
let prefix_str = match &metric.prefix {
Some(prefix) => parser.eval_str(prefix).unwrap(),
None => ExprResult::Empty,
};
for (i, item) in v.iter().enumerate() {
let name = match &prefix_str {
ExprResult::StrVector(v) => format!("{:?}#{}", v[i], metric.name),
ExprResult::Empty => {
let res = format!("{}#{}", idx, metric.name);
idx += 1;
res
}
_ => {
panic!("A label must be a string");
}
};
let m = Perfdata {
name,
@ -180,7 +184,7 @@ impl Command {
metrics.push(m);
}
}
ExprResult::Scalar(s) => {
ExprResult::Number(s) => {
let name = match &metric.prefix {
Some(prefix) => {
format!("{:?}#{}", prefix, metric.name)
@ -214,7 +218,7 @@ impl Command {
let max = if let Some(max_expr) = metric.max_expr.as_ref() {
let res = parser.eval(&max_expr).unwrap();
Some(match res {
ExprResult::Scalar(v) => v,
ExprResult::Number(v) => v,
ExprResult::Vector(v) => {
assert!(v.len() == 1);
v[0]
@ -229,7 +233,7 @@ impl Command {
let min = if let Some(min_expr) = metric.min_expr.as_ref() {
let res = parser.eval(&min_expr).unwrap();
Some(match res {
ExprResult::Scalar(v) => v,
ExprResult::Number(v) => v,
ExprResult::Vector(v) => {
assert!(v.len() == 1);
v[0]
@ -265,7 +269,7 @@ impl Command {
metrics.push(m);
}
}
ExprResult::Scalar(s) => {
ExprResult::Number(s) => {
let name = &metric.name;
let m = Perfdata {
name: name.to_string(),

View File

@ -80,38 +80,36 @@ fn main() {
loop {
let arg = parser.next();
match arg {
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();
trace!("port: {}", port);
}
Short('j') | Long("json") => {
json = Some(parser.value().unwrap().into_string().unwrap());
trace!("json: {:?}", json);
}
Short('v') | Long("snmp-version") => {
snmp_version = parser.value().unwrap().into_string().unwrap();
trace!("snmp_version: {}", snmp_version);
}
Short('c') | Long("snmp-community") => {
snmp_community = parser.value().unwrap().into_string().unwrap();
trace!("snmp_community: {}", snmp_community);
}
_ => {
debug!("other unknown argument");
}
},
None => {
break;
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();
trace!("port: {}", port);
}
Short('j') | Long("json") => {
json = Some(parser.value().unwrap().into_string().unwrap());
trace!("json: {:?}", json);
}
Short('v') | Long("snmp-version") => {
snmp_version = parser.value().unwrap().into_string().unwrap();
trace!("snmp_version: {}", snmp_version);
}
Short('c') | Long("snmp-community") => {
snmp_community = parser.value().unwrap().into_string().unwrap();
trace!("snmp_community: {}", snmp_community);
}
_ => {
debug!("other unknown argument");
}
},
None => {
break;
}
}
},
Err(err) => {
println!("err: {:?}", err);
std::process::exit(3);

View File

@ -457,7 +457,8 @@ impl SnmpResult {
self.items
.entry(key)
.and_modify(|e| match e {
ExprResult::Scalar(_) => panic!("Should not arrive"),
ExprResult::Number(_) => panic!("Should not arrive"),
ExprResult::Str(_) => panic!("Should not arrive"),
ExprResult::Vector(v) => v.push(match &typ {
ValueType::Float(f) => *f,
ValueType::None(()) => {
@ -480,6 +481,9 @@ impl SnmpResult {
panic!("Value should be a string");
}
}),
ExprResult::Empty => {
panic!("Value from SNMP query cannot be empty");
}
})
.or_insert(match typ {
ValueType::Float(f) => ExprResult::Vector(vec![f]),
@ -569,7 +573,7 @@ impl SnmpResult {
self.items
.entry(key)
.and_modify(|e| match e {
ExprResult::Scalar(_) => panic!("Should not arrive"),
ExprResult::Number(_) => panic!("Should not arrive"),
ExprResult::Vector(v) => v.push(match &typ {
ValueType::Float(f) => *f,
ValueType::None(()) => {
@ -580,6 +584,7 @@ impl SnmpResult {
}
ValueType::Integer(i) => *i as f64,
}),
ExprResult::Str(_) => panic!("Should not arrive"),
ExprResult::StrVector(v) => v.push(match &typ {
ValueType::Float(_) => {
panic!("Value should be a string");
@ -592,6 +597,9 @@ impl SnmpResult {
panic!("Value should be a string");
}
}),
ExprResult::Empty => {
panic!("Value should not be empty");
}
})
.or_insert(match typ {
ValueType::Float(f) => ExprResult::Vector(vec![f]),
@ -712,7 +720,7 @@ impl SnmpResult {
self.items
.entry(key)
.and_modify(|e| match e {
ExprResult::Scalar(_) => panic!("Should not arrive"),
ExprResult::Number(_) => panic!("Should not arrive"),
ExprResult::Vector(v) => v.push(match &typ {
ValueType::Float(f) => *f,
ValueType::None(()) => {
@ -723,6 +731,7 @@ impl SnmpResult {
}
ValueType::Integer(i) => *i as f64,
}),
ExprResult::Str(_) => panic!("Should not arrive"),
ExprResult::StrVector(v) => v.push(match &typ {
ValueType::Float(_) => {
panic!("Value should be a string");
@ -735,6 +744,9 @@ impl SnmpResult {
panic!("Value should be a string");
}
}),
ExprResult::Empty => {
panic!("Value should be a string");
}
})
.or_insert(match typ {
ValueType::Float(f) => ExprResult::Vector(vec![f]),