wip(generic-snmp): new lexer

This commit is contained in:
David Boucher 2025-04-14 10:43:32 +02:00
parent 65f386159b
commit d11c4c3e89
6 changed files with 659 additions and 663 deletions

View File

@ -0,0 +1,176 @@
use log::debug;
use std::str;
pub type Spanned<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
#[derive(Debug, PartialEq)]
pub enum Tok {
Num,
Id,
OpStar,
OpSlash,
OpPlus,
OpMinus,
LParen,
RParen,
}
#[derive(Debug, PartialEq)]
pub enum LexicalError {
NotPossible,
// Not possible
}
#[derive(Debug)]
pub struct Lexer<'input> {
chars: &'input str,
offset: usize,
}
impl<'input> Lexer<'input> {
pub fn new(input: &'input str) -> Self {
Lexer {
chars: input,
offset: 0,
}
}
fn number(&mut self, start: usize, chars: &[u8]) -> Option<Spanned<Tok, usize, LexicalError>> {
// Consume digits and decimal points
let mut end = start;
let mut done = false;
for c in chars[(start + 1)..].iter() {
end += 1;
if !(*c).is_ascii_digit() && *c != b'.' {
done = true;
break;
}
}
if !done {
end = chars.len();
}
debug!(
"Token Number from {} to {} with value '{}'",
start,
end,
str::from_utf8(&chars[start..end]).unwrap_or("Bad value")
);
self.offset = end;
Some(Ok((start, Tok::Num, end)))
}
fn identifier(
&mut self,
start: usize,
chars: &[u8],
) -> Option<Spanned<Tok, usize, LexicalError>> {
// Consume identifier
let mut end = start;
let mut done = false;
for c in chars[(start + 1)..].iter() {
end += 1;
if !c.is_ascii_alphanumeric() && *c != b'_' {
done = true;
break;
}
}
if !done {
end = chars.len();
}
debug!(
"Token Identifier from {} to {} with value '{}'",
start,
end,
str::from_utf8(&chars[start..end]).unwrap_or("Bad value")
);
self.offset = end;
Some(Ok((start, Tok::Id, end)))
}
}
impl<'input> Iterator for Lexer<'input> {
type Item = Spanned<Tok, usize, LexicalError>;
fn next(&mut self) -> Option<Self::Item> {
for (i, c) in self.chars.as_bytes().iter().enumerate().skip(self.offset) {
match *c {
b' ' | b'\t' => continue,
b'*' => {
self.offset = i + 1;
return Some(Ok((i, Tok::OpStar, i + 1)));
}
b'/' => {
self.offset = i + 1;
return Some(Ok((i, Tok::OpSlash, i + 1)));
}
b'+' => {
self.offset = i + 1;
return Some(Ok((i, Tok::OpPlus, i + 1)));
}
b'-' => {
self.offset = i + 1;
return Some(Ok((i, Tok::OpMinus, i + 1)));
}
b'(' => {
self.offset = i + 1;
return Some(Ok((i, Tok::LParen, i + 1)));
}
b')' => {
self.offset = i + 1;
return Some(Ok((i, Tok::RParen, i + 1)));
}
b'0'..=b'9' => {
// Consume digits and decimal points
return self.number(i, &self.chars.as_bytes());
}
b'a'..=b'z' | b'A'..=b'Z' | b'_' => {
return self.identifier(i, &self.chars.as_bytes());
}
_ => {
// Unknown character
debug!("Unknown character at {}: '{}'", i, *c as char);
self.offset = i + 1;
return Some(Err(LexicalError::NotPossible));
}
}
}
// No more characters to process
return None;
}
}
mod Test {
use super::*;
#[test]
fn test_lexer_num_id_num() {
let input = "123 abc 456";
let mut lexer = Lexer::new(input);
assert_eq!(lexer.next(), Some(Ok((0, Tok::Num, 3))));
assert_eq!(lexer.next(), Some(Ok((4, Tok::Id, 7))));
assert_eq!(lexer.next(), Some(Ok((8, Tok::Num, 11))));
assert_eq!(lexer.next(), None);
}
#[test]
fn test_lexer_id_num_id() {
let input = "abc 123 def";
let mut lexer = Lexer::new(input);
assert_eq!(lexer.next(), Some(Ok((0, Tok::Id, 3))));
assert_eq!(lexer.next(), Some(Ok((4, Tok::Num, 7))));
assert_eq!(lexer.next(), Some(Ok((8, Tok::Id, 11))));
}
#[test]
fn test_lexer_num_op() {
let input = "1+2*3";
let mut lexer = Lexer::new(input);
assert_eq!(lexer.next(), Some(Ok((0, Tok::Num, 1))));
assert_eq!(lexer.next(), Some(Ok((1, Tok::OpPlus, 2))));
assert_eq!(lexer.next(), Some(Ok((2, Tok::Num, 3))));
assert_eq!(lexer.next(), Some(Ok((3, Tok::OpStar, 4))));
assert_eq!(lexer.next(), Some(Ok((4, Tok::Num, 5))));
assert_eq!(lexer.next(), None);
}
}

View File

@ -1,4 +1,10 @@
mod lexer;
use lalrpop_util::lalrpop_mod;
use serde::Deserialize;
use snmp::SnmpResult;
lalrpop_mod!(grammar);
#[derive(Deserialize, Debug)]
pub struct Metric {
@ -15,3 +21,88 @@ pub struct Compute {
pub metrics: Vec<Metric>,
pub aggregations: Option<Vec<Metric>>,
}
pub struct Parser<'a> {
collect: &'a Vec<SnmpResult>,
parser: grammar::ExprParser,
}
impl<'a> Parser<'a> {
pub fn new(collect: &'a Vec<SnmpResult>) -> Parser<'a> {
Parser {
collect,
parser: grammar::ExprParser::new(),
}
}
pub fn eval(&self, expr: &str) -> f32 {
2.0_f32
}
}
mod Test {
use super::*;
#[test]
fn term() {
assert!(grammar::ExprParser::new().parse("132").is_ok());
assert!(grammar::ExprParser::new().parse("((132))").is_ok());
assert!(grammar::ExprParser::new().parse("((132)))").is_err());
}
#[test]
fn sum() {
let res = grammar::ExprParser::new().parse("1 + 2");
assert!(res.is_ok());
assert!(res.unwrap() == 3_f32);
let res = grammar::ExprParser::new().parse("1 + 2 + 3");
assert!(res.is_ok());
assert!(res.unwrap() == 6_f32);
let res = grammar::ExprParser::new().parse("1 - 2 + 3");
assert!(res.is_ok());
assert!(res.unwrap() == 2_f32);
let res = grammar::ExprParser::new().parse("1 + 2 - 3");
assert!(res.is_ok());
assert!(res.unwrap() == 0_f32);
}
#[test]
fn product() {
let res = grammar::ExprParser::new().parse("2 * 3");
assert!(res.is_ok());
assert!(res.unwrap() == 6_f32);
let res = grammar::ExprParser::new().parse("2 * 3 * 4");
assert!(res.is_ok());
assert!(res.unwrap() == 24_f32);
let res = grammar::ExprParser::new().parse("2 * 3 / 2");
assert!(res.is_ok());
assert!(res.unwrap() == 3_f32);
// let res = grammar::ProductParser::new().parse("2 / 0");
// assert!(res.is_err());
}
#[test]
fn sum_product() {
let res = grammar::ExprParser::new().parse("1 + 2 * 3");
assert!(res.is_ok());
assert!(res.unwrap() == 7_f32);
let res = grammar::ExprParser::new().parse("1 + (3 + 2 * 3) / 3");
assert!(res.is_ok());
assert!(res.unwrap() == 4_f32);
}
#[test]
fn function() {
let res = grammar::ExprParser::new().parse("Average(1, 2, 3)");
assert!(res.is_ok());
assert!(res.unwrap() == 2_f32);
let res = grammar::ExprParser::new().parse("Average(1 + 2 * 2, 3, 4)");
assert!(res.is_ok());
assert!(res.unwrap() == 4_f32);
}
}

View File

@ -1,10 +1,9 @@
extern crate serde;
extern crate serde_json;
use compute::Compute;
use compute::{Compute, Parser};
use serde::Deserialize;
use snmp::{snmp_bulk_get, snmp_bulk_walk, snmp_bulk_walk_with_labels, SnmpResult, SnmpValue};
use snmp::{snmp_bulk_get, snmp_bulk_walk, snmp_bulk_walk_with_labels};
use std::collections::{BTreeMap, HashMap};
#[derive(Copy, Clone, PartialEq)]
@ -99,67 +98,6 @@ fn compute_status(value: f32, warn: &Option<String>, crit: &Option<String>) -> S
Status::Ok
}
//fn build_metrics<'a>(
// values: &Vec<(String, f32)>,
// ag: &Option<(&str, usize, f32)>,
// ext: &'a CommandExt,
//) -> (Vec<Metric<'a>>, Status) {
// let mut metrics: Vec<Metric> = Vec::new();
// let mut status = Status::Ok;
// 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(Metric {
// name: a.0.to_string(),
// value: a.2,
// warning: w,
// critical: c,
// status: current_status,
// agregated: true,
// });
// status = worst(current_status, status);
// }
// }
// None => (),
// }
// values.iter().enumerate().for_each(|(i, v)| {
// let current_status = compute_status(v.1, &ext.warning_core, &ext.critical_core);
// metrics.push(Metric {
// name: values[i].0.clone(),
// value: v.1,
// warning: &ext.warning_core,
// critical: &ext.critical_core,
// status: current_status,
// agregated: false,
// });
// status = worst(current_status, status);
// });
// match ag {
// Some(a) => {
// if a.1 > 0 {
// let current_status =
// compute_status(a.2, &ext.warning_agregation, &ext.critical_agregation);
// metrics.push(Metric {
// name: a.0.to_string(),
// value: a.2,
// warning: &ext.warning_agregation,
// critical: &ext.critical_agregation,
// status: current_status,
// agregated: true,
// });
// status = worst(current_status, status);
// }
// }
// None => (),
// }
// (metrics, status)
//}
impl Command {
pub fn execute(
&self,
@ -181,239 +119,34 @@ impl Command {
);
collect.push(r);
} else {
let r = snmp_bulk_walk(target, version, community, &s.oid);
let r = snmp_bulk_walk(target, version, community, &s.oid, &s.name);
collect.push(r);
}
// match r.variables.len() {
// 1 => {
// let result = r.variables.get(0).unwrap();
// if let SnmpValue::Integer(v) = result.value {
// res.insert(s.name.clone(), v);
// }
// }
// _ => {
// for (idx, result) in r.variables.iter().enumerate() {
// if let SnmpValue::Integer(v) = result.value {
// res.insert(format!("{}_{}", &s.name, idx), v);
// }
// }
// }
// }
}
QueryType::Get => {
to_get.push(s.oid.as_str());
get_name.push(&s.name);
get_name.push(s.name.as_str());
}
}
}
println!("{:#?}", collect);
//let mut variables = HashMap::new();
//for res in collect.iter_mut() {
// variables.extend(res.get_variables());
//}
for metric in &self.compute.metrics {
if let Some(prefix) = &metric.prefix {}
let p = Parser::new(&collect);
for (idx, metric) in self.compute.metrics.iter().enumerate() {
let name = match &metric.prefix {
Some(prefix) => format!("{}#{}", p.eval(prefix), metric.name),
None => format!("{}#{}", idx, metric.name),
};
}
if !to_get.is_empty() {
// let r = snmp_bulk_get(target, version, community, 1, 1, &to_get);
// for (idx, result) in r.variables.iter().enumerate() {
// if let SnmpValue::Integer(v) = result.value {
// res.insert(get_name[idx].to_string(), v);
// }
// }
let r = snmp_bulk_get(target, version, community, 1, 1, &to_get, &get_name);
collect.push(r);
}
// let mut aggregation = ("", 0, Operation::None);
// let mut res: Option<(&str, SnmpResult)> = None;
// for (idx, entry) in self.leaf.entries.iter().enumerate() {
// match entry {
// Entry::Agregation(op) => {
// agregation = (&op.name, idx, op.op);
// }
// Entry::Query(query) => match query.query {
// QueryType::Walk => {
// res = Some((
// &query.name,
// snmp_bulk_walk(target, version, community, &query.oid),
// ));
// }
// },
// }
// }
// match res {
// Some(r) => {
// let mut values: Vec<(String, f32)> = Vec::new();
// let mut idx = 0;
// r.1.variables.iter().for_each(|v| {
// let label = r.0.replace("{idx}", &idx.to_string());
// 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().map(|(_, v)| v).sum();
// let avg = sum / values.len() as f32;
// Some((agregation.0, agregation.1, avg))
// }
// _ => None,
// };
// let (metrics, status) = build_metrics(&values, &ag, &ext);
// let output = self.build_output(count, status, &metrics, &ag, &ext);
// return CmdResult { status, output };
// }
// None => {
// return CmdResult {
// status: Status::Unknown,
// output: "No result".to_string(),
// };
// }
// }
println!("{:#?}", collect);
CmdResult {
status: Status::Unknown,
output: "No result".to_string(),
}
}
// fn build_output(
// &self,
// count: usize,
// status: Status,
// metrics: &Vec<Metric>,
// ag: &Option<(&str, usize, f32)>,
// ext: &CommandExt,
// ) -> String {
// let no_threshold = ext.warning_core.is_none()
// && ext.critical_core.is_none()
// && ext.warning_agregation.is_none()
// && ext.critical_agregation.is_none();
// let write_details =
// no_threshold || (ext.warning_core.is_some() || ext.critical_core.is_some());
// let write_agregation_details =
// no_threshold || (ext.warning_agregation.is_some() || ext.critical_agregation.is_some());
// let mut output_text = "".to_string();
// let mut begun = false;
// if &self.leaf.output.header != "" {
// output_text = self.leaf.output.header.replace("{status}", status.as_str());
// }
// for line in &self.leaf.output.text {
// if line.contains("idx") {
// if write_details {
// // We have to iterate on metrics
// let mut output_vec = (Vec::new(), Vec::new(), Vec::new());
// let mut idx = 0;
// for m in metrics.iter() {
// if !m.agregated {
// let text = line
// .replace("{idx}", idx.to_string().as_str())
// .replace("{name}", m.name.as_str())
// .replace("{value}", format!("{:.2}", m.value).as_str())
// .replace("{count}", count.to_string().as_str());
// match m.status {
// Status::Ok => {
// output_vec.0.push(text);
// }
// Status::Warning => {
// output_vec.1.push(text);
// }
// Status::Critical => {
// output_vec.2.push(text);
// }
// Status::Unknown => (),
// }
// idx += 1;
// }
// }
// if !output_vec.2.is_empty() {
// if begun {
// output_text += " - ";
// } else {
// begun = true;
// }
// output_text += output_vec.2.join(" - ").as_str();
// }
// if !output_vec.1.is_empty() {
// if begun {
// output_text += " - ";
// } else {
// begun = true;
// }
// output_text += output_vec.1.join(" - ").as_str();
// }
// if !output_vec.0.is_empty() {
// if begun {
// output_text += " - ";
// }
// output_text += output_vec.0.join(" - ").as_str();
// }
// }
// } else {
// if write_agregation_details {
// match ag {
// Some(a) => {
// output_text += line
// .replace(
// format!("{{{}}}", a.0).as_str(),
// format!("{:.2}", a.2).as_str(),
// )
// .replace("{count}", count.to_string().as_str())
// .as_str();
// begun = true;
// }
// None => output_text += line,
// };
// }
// }
// }
//
// let mut perfdata = " |".to_string();
// match &self.leaf.data {
// Some(d) => {
// metrics.iter().for_each(|m| {
// perfdata += format!(
// " '{}'={}{};{};{};{};{}",
// m.name,
// m.value,
// d.uom,
// match m.warning {
// Some(m) => m.to_string(),
// None => "".to_string(),
// },
// match m.critical {
// Some(m) => m.to_string(),
// None => "".to_string(),
// },
// match d.min {
// Some(m) => m.to_string(),
// None => "".to_string(),
// },
// match d.max {
// Some(m) => m.to_string(),
// None => "".to_string(),
// },
// )
// .as_str();
// });
// }
// None => {
// metrics.iter().for_each(|m| {
// perfdata += format!(
// " '{}'={};{};{}",
// m.name,
// m.value,
// match m.warning {
// Some(v) => v.to_string(),
// None => "".to_string(),
// },
// match m.critical {
// Some(v) => v.to_string(),
// None => "".to_string(),
// }
// )
// .as_str();
// });
// }
// };
// output_text + &perfdata
// }
}

View File

@ -2,19 +2,19 @@ use std::str::FromStr;
grammar;
pub Sum: f32 = {
Sum: f32 = {
<s:Sum> "+" <p:Product> => s + p,
<s:Sum> "-" <p:Product> => s - p,
<p:Product> => p,
};
pub Product: f32 = {
<p:Product> "*" <t:Term> => p * t,
<p:Product> "/" <t:Term> => p / t,
<t:Term> => t,
Product: f32 = {
<p:Product> "*" <t:Expr> => p * t,
<p:Product> "/" <t:Expr> => p / t,
<t:Expr> => t,
};
pub Args = Comma<Sum>; // (0)
Args = Comma<Sum>; // (0)
Comma<T>: Vec<f32> = { // (1)
<mut v:(<T> ",")*> <e:T?> => match e { // (2)
@ -26,10 +26,19 @@ Comma<T>: Vec<f32> = { // (1)
}
};
pub Term: f32 = {
pub Expr: f32 = {
<n:Num> => n,
"(" <t:Sum> ")" => t,
"Average" "(" <a:Args> ")" => { let sum:f32 = a.iter().sum(); sum / a.len() as f32 }
};
Num: f32 = <s:r"-?[0-9.]+"> => f32::from_str(s).unwrap();
match {
r"-?[0-9.]+" => NUM,
} else {
r"\w+" => ID,
_
}
Num: f32 = <s:NUM> => f32::from_str(s).unwrap();
Identifier: String = <s:ID> => s.to_string();

View File

@ -135,70 +135,3 @@ fn main() {
//println!("{}", result.output);
//std::process::exit(result.status as i32);
}
mod Test {
use super::*;
#[test]
fn term() {
assert!(grammar::TermParser::new().parse("132").is_ok());
assert!(grammar::TermParser::new().parse("((132))").is_ok());
assert!(grammar::TermParser::new().parse("((132)))").is_err());
}
#[test]
fn sum() {
let res = grammar::SumParser::new().parse("1 + 2");
assert!(res.is_ok());
assert!(res.unwrap() == 3_f32);
let res = grammar::SumParser::new().parse("1 + 2 + 3");
assert!(res.is_ok());
assert!(res.unwrap() == 6_f32);
let res = grammar::SumParser::new().parse("1 - 2 + 3");
assert!(res.is_ok());
assert!(res.unwrap() == 2_f32);
let res = grammar::SumParser::new().parse("1 + 2 - 3");
assert!(res.is_ok());
assert!(res.unwrap() == 0_f32);
}
#[test]
fn product() {
let res = grammar::ProductParser::new().parse("2 * 3");
assert!(res.is_ok());
assert!(res.unwrap() == 6_f32);
let res = grammar::ProductParser::new().parse("2 * 3 * 4");
assert!(res.is_ok());
assert!(res.unwrap() == 24_f32);
let res = grammar::ProductParser::new().parse("2 * 3 / 2");
assert!(res.is_ok());
assert!(res.unwrap() == 3_f32);
// let res = grammar::ProductParser::new().parse("2 / 0");
// assert!(res.is_err());
}
#[test]
fn sum_product() {
let res = grammar::SumParser::new().parse("1 + 2 * 3");
assert!(res.is_ok());
assert!(res.unwrap() == 7_f32);
let res = grammar::SumParser::new().parse("1 + (3 + 2 * 3) / 3");
assert!(res.is_ok());
assert!(res.unwrap() == 4_f32);
}
#[test]
fn function() {
let res = grammar::TermParser::new().parse("Average(1, 2, 3)");
assert!(res.is_ok());
assert!(res.unwrap() == 2_f32);
let res = grammar::TermParser::new().parse("Average(1 + 2 * 2, 3, 4)");
assert!(res.is_ok());
assert!(res.unwrap() == 4_f32);
}
}

View File

@ -5,13 +5,13 @@ extern crate rasn_snmp;
use log::{info, trace, warn};
use rasn::types::ObjectIdentifier;
use rasn_snmp::v2::BulkPdu;
use rasn_snmp::v2::Pdus;
use rasn_snmp::v2::VarBind;
use rasn_snmp::v2::VarBindValue;
use rasn_snmp::v2::{BulkPdu, Pdu};
use rasn_snmp::v2::{GetBulkRequest, GetNextRequest, GetRequest};
use rasn_snmp::v2c::Message;
use std::collections::{BTreeMap, HashMap};
use std::collections::BTreeMap;
use std::convert::TryInto;
use std::net::UdpSocket;
@ -34,105 +34,6 @@ pub struct SnmpResult {
last_oid: Vec<u32>,
}
//#[derive(Debug, PartialEq)]
//pub struct SnmpSubResult<'a> {
// pub label: &'a str,
// pub value: &'a str,
// pub variables: Vec<SnmpVariable>,
//}
//#[derive(Debug, PartialEq)]
//pub struct SnmpResult<'a> {
// pub labels: Vec<SnmpSubResult<'a>>,
// last_oid: Vec<u32>,
//}
#[derive(Debug, PartialEq)]
pub enum SnmpValue {
Integer(i64),
String(String),
}
#[derive(Debug, PartialEq)]
pub struct SnmpVariable {
pub name: String,
pub value: SnmpValue,
}
//impl<'a> SnmpResult<'a> {
// fn new(labels: &'a BTreeMap<String, String>) -> SnmpResult<'a> {
// let mut retval: Vec<SnmpSubResult> = Vec::new();
// retval.reserve(labels.len());
// for (label, value) in labels.iter() {
// let item = SnmpSubResult {
// label: &label,
// value: &value,
// variables: Vec::new(),
// };
// retval.push(item);
// }
// SnmpResult {
// labels: retval,
// last_oid: Vec::new(),
// }
// }
//
// fn new_simple() -> SnmpResult<'a> {
// let label = SnmpSubResult {
// label: "",
// value: "",
// variables: Vec::new(),
// };
// SnmpResult {
// labels: vec![label],
// last_oid: Vec::new(),
// }
// }
//
// fn add_variable(&mut self, idx: usize, name: String, value: SnmpValue) {
// self.labels[idx]
// .variables
// .push(SnmpVariable { name, value });
// }
//
// fn get_last_oid(&self, idx: usize) -> &str {
// if self.labels.is_empty() {
// return "";
// }
// let last = self.labels[idx].variables.iter().last().unwrap();
// if last.name.is_empty() {
// return "";
// }
// last.name.as_str()
// }
//
// fn merge(&mut self, mut other: SnmpResult) {
// for v in other.labels.iter_mut() {
// for vv in self.labels.iter_mut() {
// if v.label == vv.label {
// while !v.variables.is_empty() {
// let last = v.variables.pop().unwrap();
// vv.variables.push(last);
// }
// break;
// }
// }
// }
// self.last_oid = other.last_oid;
// }
//
// pub fn get_variables(&self) -> HashMap<String, SnmpValue> {
// let retval = HashMap::new();
// // for r in self.labels.iter() {
// // for s in r.variables.iter() {
// // let name = format!("{}.{}", self.name, r.label, s.label);
// // retval.insert(name, s.value);
// // }
// // }
// retval
// }
//}
//pub fn snmp_get(target: &str, oid: &str, community: &str) -> SnmpResult {
// let oid_tab = oid
// .split('.')
@ -266,63 +167,63 @@ pub fn snmp_bulk_get<'a>(
non_repeaters: u32,
max_repetitions: u32,
oid: &Vec<&str>,
names: &Vec<&str>,
) -> SnmpResult {
// let mut oids_tab = oid
// .iter()
// .map(|x| {
// x.split('.')
// .map(|x| x.parse::<u32>().unwrap())
// .collect::<Vec<u32>>()
// })
// .collect::<Vec<Vec<u32>>>();
//
let mut oids_tab = oid
.iter()
.map(|x| {
x.split('.')
.map(|x| x.parse::<u32>().unwrap())
.collect::<Vec<u32>>()
})
.collect::<Vec<Vec<u32>>>();
let mut retval = SnmpResult {
items: BTreeMap::new(),
last_oid: Vec::new(),
};
// let mut request_id: i32 = 1;
//
// let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
// socket.connect(target).expect("connect function failed");
// let duration = std::time::Duration::from_millis(1000);
// socket.set_read_timeout(Some(duration)).unwrap();
//
// let variable_bindings = oids_tab
// .iter()
// .map(|x| VarBind {
// name: ObjectIdentifier::new_unchecked(x.to_vec().into()),
// value: VarBindValue::Unspecified,
// })
// .collect::<Vec<VarBind>>();
//
// let pdu = BulkPdu {
// request_id,
// non_repeaters,
// max_repetitions,
// variable_bindings,
// };
//
// let get_request: GetBulkRequest = GetBulkRequest(pdu);
//
// let message: Message<GetBulkRequest> = Message {
// version: 1.into(),
// community: community.to_string().into(),
// data: get_request.into(),
// };
//
// // Send the message through an UDP socket
// let encoded: Vec<u8> = rasn::der::encode(&message).unwrap();
// let res: usize = socket.send(&encoded).unwrap();
// assert!(res == encoded.len());
//
// let mut buf: [u8; 1024] = [0; 1024];
// let resp: (usize, std::net::SocketAddr) = socket.recv_from(buf.as_mut_slice()).unwrap();
//
// trace!("Received {} bytes", resp.0);
// assert!(resp.0 > 0);
// let decoded: Message<Pdus> = rasn::ber::decode(&buf[0..resp.0]).unwrap();
// let (result, _completed) = build_response(decoded, "", false);
// retval.merge(result);
let mut request_id: i32 = 1;
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
socket.connect(target).expect("connect function failed");
let duration = std::time::Duration::from_millis(1000);
socket.set_read_timeout(Some(duration)).unwrap();
let variable_bindings = oids_tab
.iter()
.map(|x| VarBind {
name: ObjectIdentifier::new_unchecked(x.to_vec().into()),
value: VarBindValue::Unspecified,
})
.collect::<Vec<VarBind>>();
let pdu = BulkPdu {
request_id,
non_repeaters,
max_repetitions,
variable_bindings,
};
let get_request: GetBulkRequest = GetBulkRequest(pdu);
let message: Message<GetBulkRequest> = Message {
version: 1.into(),
community: community.to_string().into(),
data: get_request.into(),
};
// Send the message through an UDP socket
let encoded: Vec<u8> = rasn::der::encode(&message).unwrap();
let res: usize = socket.send(&encoded).unwrap();
assert!(res == encoded.len());
let mut buf: [u8; 1024] = [0; 1024];
let resp: (usize, std::net::SocketAddr) = socket.recv_from(buf.as_mut_slice()).unwrap();
trace!("Received {} bytes", resp.0);
assert!(resp.0 > 0);
let decoded: Message<Pdus> = rasn::ber::decode(&buf[0..resp.0]).unwrap();
let _completed = retval.build_response_with_names(decoded, "", names, false);
retval
}
@ -342,75 +243,68 @@ pub fn snmp_bulk_get<'a>(
/// use snmp_rust::snmp_bulk_walk;
/// let result = snmp_bulk_walk("127.0.0.1:161", "2c", "public", "1.3.6.1.2.1.25.3.3.1.2");
/// ```
pub fn snmp_bulk_walk<'a>(target: &str, _version: &str, community: &str, oid: &str) -> SnmpResult {
let mut oid_tab = oid
pub fn snmp_bulk_walk<'a>(
target: &str,
_version: &str,
community: &str,
oid: &str,
snmp_name: &str,
) -> SnmpResult {
let oid_init = oid
.split('.')
.map(|x| x.parse::<u32>().unwrap())
.collect::<Vec<u32>>();
let mut oid_tab = &oid_init;
let mut retval = SnmpResult {
items: BTreeMap::new(),
last_oid: Vec::new(),
};
// let mut request_id: i32 = 1;
//
// let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
// socket.connect(target).expect("connect function failed");
// let duration = std::time::Duration::from_millis(1000);
// socket.set_read_timeout(Some(duration)).unwrap();
//
// loop {
// let variable_bindings = vec![VarBind {
// name: ObjectIdentifier::new_unchecked(oid_tab.to_vec().into()),
// value: VarBindValue::Unspecified,
// }];
//
// let pdu = BulkPdu {
// request_id,
// non_repeaters: 0,
// max_repetitions: 10,
// variable_bindings,
// };
//
// let get_request: GetBulkRequest = GetBulkRequest(pdu);
//
// let message: Message<GetBulkRequest> = Message {
// version: 1.into(),
// community: community.to_string().into(),
// data: get_request.into(),
// };
//
// // Send the message through an UDP socket
// let encoded: Vec<u8> = rasn::der::encode(&message).unwrap();
// let res: usize = socket.send(&encoded).unwrap();
// assert!(res == encoded.len());
//
// let mut buf: [u8; 1024] = [0; 1024];
// let resp: (usize, std::net::SocketAddr) = socket.recv_from(buf.as_mut_slice()).unwrap();
//
// trace!("Received {} bytes", resp.0);
// assert!(resp.0 > 0);
// let decoded: Message<Pdus> = rasn::ber::decode(&buf[0..resp.0]).unwrap();
// if let Pdus::Response(resp) = &decoded.data {
// let resp_oid = &resp.0.variable_bindings[0].name;
// let n = resp_oid.len() - 1;
// }
// let (result, completed) = build_response(decoded, &oid, true);
// retval.merge(result);
// if completed {
// break;
// }
// if !retval.variables.is_empty() {
// oid_tab = retval
// .variables
// .last()
// .unwrap()
// .name
// .split('.')
// .map(|x| x.parse::<u32>().unwrap())
// .collect::<Vec<u32>>();
// }
// }
let request_id: i32 = 1;
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
socket.connect(target).expect("connect function failed");
let duration = std::time::Duration::from_millis(1000);
socket.set_read_timeout(Some(duration)).unwrap();
loop {
let variable_bindings = vec![VarBind {
name: ObjectIdentifier::new_unchecked(oid_tab.to_vec().into()),
value: VarBindValue::Unspecified,
}];
let pdu = BulkPdu {
request_id,
non_repeaters: 0,
max_repetitions: 10,
variable_bindings,
};
let get_request: GetBulkRequest = GetBulkRequest(pdu);
let message: Message<GetBulkRequest> = Message {
version: 1.into(),
community: community.to_string().into(),
data: get_request.into(),
};
// Send the message through an UDP socket
let encoded: Vec<u8> = rasn::der::encode(&message).unwrap();
let res: usize = socket.send(&encoded).unwrap();
assert!(res == encoded.len());
let mut buf: [u8; 1024] = [0; 1024];
let resp: (usize, std::net::SocketAddr) = socket.recv_from(buf.as_mut_slice()).unwrap();
trace!("Received {} bytes", resp.0);
assert!(resp.0 > 0);
let decoded: Message<Pdus> = rasn::ber::decode(&buf[0..resp.0]).unwrap();
let completed = retval.build_response(decoded, &oid, snmp_name, true);
if completed {
break;
}
oid_tab = &retval.last_oid;
}
retval
}
@ -471,10 +365,6 @@ pub fn snmp_bulk_walk_with_labels<'a>(
trace!("Received {} bytes", resp.0);
assert!(resp.0 > 0);
let decoded: Message<Pdus> = rasn::ber::decode(&buf[0..resp.0]).unwrap();
//if let Pdus::Response(resp) = &decoded.data {
// let resp_oid = &resp.0.variable_bindings[0].name;
// let oid_tab = &resp.0.variable_bindings.last().unwrap().name;
//}
let completed = retval.build_response_with_labels(decoded, &oid, snmp_name, labels, true);
if completed {
break;
@ -600,95 +490,259 @@ impl SnmpResult {
}
completed
}
fn build_response_with_names<'a>(
&mut self,
decoded: Message<Pdus>,
oid: &str,
names: &Vec<&str>,
walk: bool,
) -> bool {
let mut completed = false;
if let Pdus::Response(resp) = &decoded.data {
let vars = &resp.0.variable_bindings;
for (idx, var) in vars.iter().enumerate() {
let name = var.name.to_string();
self.last_oid = name
.split('.')
.map(|x| x.parse::<u32>().unwrap())
.collect::<Vec<u32>>();
if walk {
if !name.starts_with(oid) {
completed = true;
break;
}
}
let prefix: &str = &name[..name.rfind('.').unwrap()];
let mut typ = ValueType::None(());
let value = match &var.value {
VarBindValue::Unspecified => {
warn!("Unspecified");
}
VarBindValue::NoSuchObject => {
warn!("NoSuchObject");
}
VarBindValue::NoSuchInstance => {
warn!("NoSuchInstance");
}
VarBindValue::EndOfMibView => {
warn!("EndOfMibView");
}
VarBindValue::Value(value) => {
warn!("Value {:?}", &value);
match value {
rasn_smi::v2::ObjectSyntax::Simple(value) => {
info!("Simple {:?}", value);
match value {
rasn_smi::v2::SimpleSyntax::Integer(value) => {
typ = ValueType::Integer(value.try_into().unwrap());
}
rasn_smi::v2::SimpleSyntax::String(value) => {
// We transform the value into a rust String
typ = ValueType::String(
String::from_utf8(value.to_vec()).unwrap(),
);
}
rasn_smi::v2::SimpleSyntax::ObjectId(value) => {
let oid: String =
value.iter().map(|&id| id.to_string() + ".").collect();
typ = ValueType::String(oid);
}
_ => {
typ = ValueType::String("Other".to_string());
}
}
}
_ => {
info!("other");
}
}
}
};
let key = format!("{}", names[idx]);
self.items
.entry(key)
.and_modify(|e| match e {
SnmpItem::Nbr(v) => v.push(match &typ {
ValueType::Float(f) => *f,
ValueType::None(()) => {
panic!("Should not arrive");
}
ValueType::String(_) => {
panic!("Value should be a float");
}
ValueType::Integer(i) => *i as f32,
}),
SnmpItem::Str(v) => v.push(match &typ {
ValueType::Float(_) => {
panic!("Value should be a string");
}
ValueType::None(()) => {
panic!("Should not arrive");
}
ValueType::String(s) => s.to_string(),
ValueType::Integer(_) => {
panic!("Value should be a string");
}
}),
})
.or_insert(match typ {
ValueType::Float(f) => SnmpItem::Nbr(vec![f]),
ValueType::None(()) => {
panic!("Should not arrive");
}
ValueType::String(s) => SnmpItem::Str(vec![s]),
ValueType::Integer(i) => SnmpItem::Nbr(vec![i as f32]),
});
}
}
completed
}
fn build_response<'a>(
&mut self,
decoded: Message<Pdus>,
oid: &str,
snmp_name: &str,
walk: bool,
) -> bool {
let mut completed = false;
if let Pdus::Response(resp) = &decoded.data {
let vars = &resp.0.variable_bindings;
for var in vars {
let name = var.name.to_string();
self.last_oid = name
.split('.')
.map(|x| x.parse::<u32>().unwrap())
.collect::<Vec<u32>>();
if walk {
if !name.starts_with(oid) {
completed = true;
break;
}
}
let prefix: &str = &name[..name.rfind('.').unwrap()];
let mut typ = ValueType::None(());
let value = match &var.value {
VarBindValue::Unspecified => {
warn!("Unspecified");
}
VarBindValue::NoSuchObject => {
warn!("NoSuchObject");
}
VarBindValue::NoSuchInstance => {
warn!("NoSuchInstance");
}
VarBindValue::EndOfMibView => {
warn!("EndOfMibView");
}
VarBindValue::Value(value) => {
warn!("Value {:?}", &value);
match value {
rasn_smi::v2::ObjectSyntax::Simple(value) => {
info!("Simple {:?}", value);
match value {
rasn_smi::v2::SimpleSyntax::Integer(value) => {
typ = ValueType::Integer(value.try_into().unwrap());
}
rasn_smi::v2::SimpleSyntax::String(value) => {
// We transform the value into a rust String
typ = ValueType::String(
String::from_utf8(value.to_vec()).unwrap(),
);
}
rasn_smi::v2::SimpleSyntax::ObjectId(value) => {
let oid: String =
value.iter().map(|&id| id.to_string() + ".").collect();
typ = ValueType::String(oid);
}
_ => {
typ = ValueType::String("Other".to_string());
}
}
}
_ => {
info!("other");
}
}
//match value {
// rasn_smi::v2::ObjectSyntax::Simple(value) => {
// info!("Simple {:?}", value);
// match value {
// rasn_smi::v2::ObjectSyntax::Simple(value) => {
// info!("Simple {:?}", value);
// match value {
// rasn_smi::v2::SimpleSyntax::Integer(value) => {
// typ = ValueType::Integer(value.try_into().unwrap());
// }
// rasn_smi::v2::SimpleSyntax::String(value) => {
// // We transform the value into a rust String
// typ = ValueType::String(
// String::from_utf8(value.to_vec()).unwrap(),
// );
// }
// rasn_smi::v2::SimpleSyntax::ObjectId(value) => {
// let oid: String = value
// .iter()
// .map(|&id| id.to_string() + ".")
// .collect();
// typ = ValueType::String(oid);
// }
// _ => {
// typ = ValueType::String("Other".to_string());
// }
// };
// }
// rasn_smi::v2::ObjectSyntax::ApplicationWide(value) => {
// info!("Application {:?}", value);
// }
// };
// }
//}
}
};
let key = format!("{}", snmp_name);
self.items
.entry(key)
.and_modify(|e| match e {
SnmpItem::Nbr(v) => v.push(match &typ {
ValueType::Float(f) => *f,
ValueType::None(()) => {
panic!("Should not arrive");
}
ValueType::String(_) => {
panic!("Value should be a float");
}
ValueType::Integer(i) => *i as f32,
}),
SnmpItem::Str(v) => v.push(match &typ {
ValueType::Float(_) => {
panic!("Value should be a string");
}
ValueType::None(()) => {
panic!("Should not arrive");
}
ValueType::String(s) => s.to_string(),
ValueType::Integer(_) => {
panic!("Value should be a string");
}
}),
})
.or_insert(match typ {
ValueType::Float(f) => SnmpItem::Nbr(vec![f]),
ValueType::None(()) => {
panic!("Should not arrive");
}
ValueType::String(s) => SnmpItem::Str(vec![s]),
ValueType::Integer(i) => SnmpItem::Nbr(vec![i as f32]),
});
}
}
completed
}
}
//fn build_response<'a>(decoded: Message<Pdus>, oid: &str, walk: bool) -> (SnmpResult, bool) {
// let mut retval = SnmpResult{items: BTreeMap::new(), last_oid: Vec::new()};
// let mut completed = false;
// let mut last_oid: &str = "";
//
// if let Pdus::Response(resp) = &decoded.data {
// let vars = &resp.0.variable_bindings;
// for var in vars {
// let name = var.name.to_string();
// if walk {
// if !name.starts_with(oid) {
// completed = true;
// last_oid = "";
// break;
// }
// }
// let mut idx: i32 = -1;
// retval.labels.iter().enumerate().for_each(|label| {
// if name.ends_with(label.1.label) {
// idx = label.0 as i32;
// return;
// }
// });
// if idx == -1 {
// continue;
// }
// match &var.value {
// VarBindValue::Unspecified => {
// warn!("Unspecified");
// }
// VarBindValue::NoSuchObject => {
// warn!("NoSuchObject");
// }
// VarBindValue::NoSuchInstance => {
// warn!("NoSuchInstance");
// }
// VarBindValue::EndOfMibView => {
// warn!("EndOfMibView");
// }
// VarBindValue::Value(value) => {
// warn!("Value {:?}", &value);
// match value {
// rasn_smi::v2::ObjectSyntax::Simple(value) => {
// info!("Simple {:?}", value);
// match value {
// rasn_smi::v2::SimpleSyntax::Integer(value) => {
// let v = value.try_into().unwrap();
// retval.add_variable(idx as usize, name, SnmpValue::Integer(v));
// last_oid = retval.get_last_oid(idx as usize);
// }
// rasn_smi::v2::SimpleSyntax::String(value) => {
// // We transform the value into a rust String
// retval.add_variable(
// idx as usize,
// name,
// SnmpValue::String(
// String::from_utf8(value.to_vec()).unwrap(),
// ),
// );
// last_oid = retval.get_last_oid(idx as usize);
// }
// rasn_smi::v2::SimpleSyntax::ObjectId(value) => {
// let oid: String =
// value.iter().map(|&id| id.to_string() + ".").collect();
// retval.add_variable(idx as usize, name, SnmpValue::String(oid));
// last_oid = retval.get_last_oid(idx as usize);
// }
// _ => {
// retval.add_variable(
// idx as usize,
// name,
// SnmpValue::String("Other".to_string()),
// );
// last_oid = retval.get_last_oid(idx as usize);
// }
// };
// }
// rasn_smi::v2::ObjectSyntax::ApplicationWide(value) => {
// info!("Application {:?}", value);
// }
// };
// }
// };
// }
// }
// (retval, completed)
//}
//mod tests {
// use super::*;
//