diff --git a/src/base_parsers.rs b/src/base_parsers.rs index 634e38b..1e4244d 100644 --- a/src/base_parsers.rs +++ b/src/base_parsers.rs @@ -128,6 +128,16 @@ where .message("while getting param") } +pub(crate) fn param_period(x: &'static str) -> impl Parser +where + I: Stream, + I::Error: ParseError, +{ + attempt(string(x).skip(token('.'))) + .map(|x| Param(x.to_string())) + .message("while getting param") +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/fixtures/valid/should-be-valid.txt b/src/fixtures/valid/should-be-valid.txt new file mode 100644 index 0000000..396fea8 --- /dev/null +++ b/src/fixtures/valid/should-be-valid.txt @@ -0,0 +1,6 @@ +obdfilter.ai400x2-OST0000.exports.0@lo.stats= +snapshot_time 1687448377.140066624 secs.nsecs +create 10 samples [usecs] 44 4724 8911 25377661 +statfs 5842 samples [usecs] 0 32132 77736 1248293008 +get_info 1 samples [usecs] 3541 3541 3541 12538681 +obdfilter.ai400-OST0001.num_exports=4 diff --git a/src/fixtures/valid/valid-single.txt b/src/fixtures/valid/valid-single.txt new file mode 100644 index 0000000..ee1215e --- /dev/null +++ b/src/fixtures/valid/valid-single.txt @@ -0,0 +1,6 @@ +obdfilter.ai400-OST0001.num_exports=4 +obdfilter.ai400x2-OST0000.exports.0@lo.stats= +snapshot_time 1687448377.140066624 secs.nsecs +create 10 samples [usecs] 44 4724 8911 25377661 +statfs 5842 samples [usecs] 0 32132 77736 1248293008 +get_info 1 samples [usecs] 3541 3541 3541 12538681 diff --git a/src/oss/obdfilter_parser.rs b/src/oss/obdfilter_parser.rs index 38a0a13..749f136 100644 --- a/src/oss/obdfilter_parser.rs +++ b/src/oss/obdfilter_parser.rs @@ -3,17 +3,19 @@ // license that can be found in the LICENSE file. use crate::{ - base_parsers::{digits, param, period, target}, + base_parsers::{digits, equals, param, param_period, period, target}, oss::job_stats, stats_parser::stats, types::{JobStatOst, Param, Record, Stat, Target, TargetStat, TargetStats, TargetVariant}, + ExportStats, }; use combine::{ choice, error::ParseError, - parser::char::{newline, string}, + many, many1, + parser::char::{alpha_num, newline, string}, stream::Stream, - Parser, + token, Parser, }; pub(crate) const JOBSTATS: &str = "job_stats"; @@ -24,13 +26,17 @@ pub(crate) const TOT_DIRTY: &str = "tot_dirty"; pub(crate) const TOT_GRANTED: &str = "tot_granted"; pub(crate) const TOT_PENDING: &str = "tot_pending"; -pub(crate) const OBD_STATS: [&str; 6] = [ +pub(crate) const EXPORTS: &str = "exports"; +pub(crate) const EXPORTS_PARAMS: &str = "exports.*.stats"; + +pub(crate) const OBD_STATS: [&str; 7] = [ JOBSTATS, STATS, NUM_EXPORTS, TOT_DIRTY, TOT_GRANTED, TOT_PENDING, + EXPORTS_PARAMS, ]; /// Takes OBD_STATS and produces a list of params for @@ -53,10 +59,35 @@ where .message("while parsing target_name") } +/// Parses the name of a target +fn exports_stat() -> impl Parser +where + I: Stream, + I::Error: ParseError, +{ + ( + many1::(alpha_num().or(token('@'))).skip(period()), + string("stats").skip(equals()), + stats(), + ) + .map(|(nid, _, stats)| ExportStats { nid, stats }) + .message("while parsing export_stats") +} + +/// Parses the name of a target +fn exports_stats() -> impl Parser> +where + I: Stream, + I::Error: ParseError, +{ + (many(exports_stat())).map(|x| x) +} + #[derive(Debug)] enum ObdfilterStat { JobStats(Option>), Stats(Vec), + ExportStats(Vec), NumExports(u64), TotDirty(u64), TotGranted(u64), @@ -90,6 +121,10 @@ where param(TOT_PENDING), digits().skip(newline()).map(ObdfilterStat::TotPending), ), + ( + param_period(EXPORTS), + exports_stats().map(ObdfilterStat::ExportStats), + ), )) .message("while parsing obdfilter") } @@ -137,6 +172,12 @@ where param, value, }), + ObdfilterStat::ExportStats(value) => TargetStats::ExportStatsOst(TargetStat { + kind: TargetVariant::Ost, + target, + param, + value, + }), }) .map(Record::Target) .message("while parsing obdfilter") diff --git a/src/parser.rs b/src/parser.rs index a443fa0..cdf097f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -145,6 +145,29 @@ osd-ldiskfs.fs2-MDT0000.kbytestotal=1819968 fn test_node_output() { let x = include_str!("./fixtures/valid/valid.txt"); + let result = parse() + .easy_parse(x) + .map_err(|err| err.map_position(|p| p.translate_position(x))) + .unwrap(); + + assert_debug_snapshot!(result); + } + #[test] + fn test_node_output_single_valid() { + let x = include_str!("./fixtures/valid/valid-single.txt"); + + let result = parse() + .easy_parse(x) + .map_err(|err| err.map_position(|p| p.translate_position(x))) + .unwrap(); + + assert_debug_snapshot!(result); + } + + #[test] + fn test_node_output_should_be_valid() { + let x = include_str!("./fixtures/valid/should-be-valid.txt"); + let result = parse() .easy_parse(x) .map_err(|err| err.map_position(|p| p.translate_position(x))) diff --git a/src/snapshots/lustre_collector__parser__tests__node_output_single_valid.snap b/src/snapshots/lustre_collector__parser__tests__node_output_single_valid.snap new file mode 100644 index 0000000..82cbed6 --- /dev/null +++ b/src/snapshots/lustre_collector__parser__tests__node_output_single_valid.snap @@ -0,0 +1,94 @@ +--- +source: src/parser.rs +expression: result +--- +( + [ + Target( + NumExports( + TargetStat { + kind: Ost, + param: Param( + "num_exports", + ), + target: Target( + "ai400-OST0001", + ), + value: 4, + }, + ), + ), + Target( + ExportStatsOst( + TargetStat { + kind: Ost, + param: Param( + "exports", + ), + target: Target( + "ai400x2-OST0000", + ), + value: [ + ExportStats { + nid: "0@lo", + stats: [ + Stat { + name: "create", + units: "usecs", + samples: 10, + min: Some( + 44, + ), + max: Some( + 4724, + ), + sum: Some( + 8911, + ), + sumsquare: Some( + 25377661, + ), + }, + Stat { + name: "statfs", + units: "usecs", + samples: 5842, + min: Some( + 0, + ), + max: Some( + 32132, + ), + sum: Some( + 77736, + ), + sumsquare: Some( + 1248293008, + ), + }, + Stat { + name: "get_info", + units: "usecs", + samples: 1, + min: Some( + 3541, + ), + max: Some( + 3541, + ), + sum: Some( + 3541, + ), + sumsquare: Some( + 12538681, + ), + }, + ], + }, + ], + }, + ), + ), + ], + "", +) diff --git a/src/snapshots/lustre_collector__parser__tests__params.snap b/src/snapshots/lustre_collector__parser__tests__params.snap index 7980461..1e893d7 100644 --- a/src/snapshots/lustre_collector__parser__tests__params.snap +++ b/src/snapshots/lustre_collector__parser__tests__params.snap @@ -27,6 +27,7 @@ expression: params() "obdfilter.*OST*.tot_dirty", "obdfilter.*OST*.tot_granted", "obdfilter.*OST*.tot_pending", + "obdfilter.*OST*.exports.*.stats", "mdt.*.job_stats", "mdt.*.md_stats", "mdt.*MDT*.num_exports", diff --git a/src/types.rs b/src/types.rs index 5582a33..6563bf4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -61,6 +61,13 @@ pub struct JobStatsOst { pub job_stats: Option>, } +#[derive(PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)] +pub struct ExportStats { + pub nid: String, + pub stats: Vec, +} + + /// Used to represent an unsigned timestamp in Lustre. /// /// Only use this field whne you are sure that the timestamp is unsigned. @@ -496,6 +503,7 @@ pub enum TargetStats { ThreadsMax(TargetStat), ThreadsStarted(TargetStat), RecoveryStatus(TargetStat), + ExportStatsOst(TargetStat>) } #[derive(PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]