diff --git a/fixtures/mdd.txt b/fixtures/mdd.txt new file mode 100644 index 0000000..34ae6c7 --- /dev/null +++ b/fixtures/mdd.txt @@ -0,0 +1,7 @@ +mdd.ai400x2-MDT0000.changelog_users= +current_index: 0 +ID index (idle) mask +cl1 0 (327) +mdd.ai400x2-MDT0001.changelog_users= +current_index: 0 +ID index (idle) mask diff --git a/src/fixtures/valid/valid.txt b/src/fixtures/valid/valid.txt index 3f44a11..071bc03 100644 --- a/src/fixtures/valid/valid.txt +++ b/src/fixtures/valid/valid.txt @@ -328,3 +328,10 @@ req_active 1 samples [reqs] 1 1 1 1 req_timeout 1 samples [secs] 15 15 15 225 reqbuf_avail 3 samples [bufs] 1 1 3 3 ldlm_bl_callback 1 samples [usecs] 16 16 16 256 +mdd.ai400x2-MDT0000.changelog_users= +current_index: 0 +ID index (idle) mask +cl1 0 (327) +mdd.ai400x2-MDT0001.changelog_users= +current_index: 0 +ID index (idle) mask diff --git a/src/lib.rs b/src/lib.rs index 998a75a..ef13ccd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ pub(crate) mod exports_parser; pub(crate) mod ldlm; pub(crate) mod llite; mod lnetctl_parser; +mod mdd_parser; mod mds; pub mod mgs; mod node_stats_parsers; diff --git a/src/mdd_parser.rs b/src/mdd_parser.rs new file mode 100644 index 0000000..01841e6 --- /dev/null +++ b/src/mdd_parser.rs @@ -0,0 +1,147 @@ +// Copyright (c) 2024 DDN. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +use crate::{ + base_parsers::{digits, param, period, target, till_newline, till_period}, + types::{Param, Record, Target, TargetStat, TargetStats, TargetVariant}, + ChangeLogUser, ChangelogStat, +}; +use combine::{ + attempt, choice, + error::{ParseError, StreamError}, + many, + parser::char::{newline, spaces, string}, + stream::{Stream, StreamErrorFor}, + token, Parser, +}; + +pub(crate) const MDD: &str = "mdd"; +pub(crate) const CHANGELOG_USERS: &str = "changelog_users"; +pub(crate) fn params() -> Vec { + vec![format!("{MDD}.*.{CHANGELOG_USERS}")] +} + +#[derive(Debug)] +enum MddStat { + /// Changelog stat + ChangeLog(ChangelogStat), +} + +fn target_and_variant() -> impl Parser +where + I: Stream, + I::Error: ParseError, +{ + ( + attempt(string("mdd").skip(till_period())).skip(period()), + target().skip(period()), + ) + .and_then(move |(_, x)| -> Result<_, _> { + let variant = match (&x).try_into() { + Ok(x) => x, + Err(e) => return Err(StreamErrorFor::::other(e)), + }; + + Ok((x, variant)) + }) + .message("while parsing target_and_variant") +} + +fn table_headers() -> impl Parser +where + I: Stream, + I::Error: ParseError, +{ + (string("ID"), till_newline()).map(|_| ()) +} + +fn table_rows() -> impl Parser> +where + I: Stream, + I::Error: ParseError, +{ + many(attempt(( + target(), + spaces(), + digits(), + spaces(), + token('('), + digits(), + token(')'), + till_newline().skip(newline()), + ))) + .map(|x: Vec<_>| { + x.iter() + .map(|x| ChangeLogUser { + user: x.0.to_string(), + index: x.2, + idle_secs: x.5, + }) + .collect() + }) +} + +fn mdd_stat() -> impl Parser +where + I: Stream, + I::Error: ParseError, +{ + choice((( + param(CHANGELOG_USERS), + ( + newline(), + string("current_index: "), + digits(), + newline(), + table_headers(), + newline(), + table_rows(), + ) + .map(|(_, _, x, _, _, _, y)| { + MddStat::ChangeLog(ChangelogStat { + current_index: x, + users: y, + }) + }), + ) + .message("while parsing changelog"),)) +} + +pub(crate) fn parse() -> impl Parser +where + I: Stream, + I::Error: ParseError, +{ + (target_and_variant(), mdd_stat()) + .map(|((target, kind), (param, stat))| match stat { + MddStat::ChangeLog(value) => TargetStats::Changelog(TargetStat { + kind, + target, + param, + value, + }), + }) + .map(Record::Target) + .message("while parsing mdd") +} + +#[cfg(test)] +mod tests { + use combine::{many, EasyParser}; + use insta::assert_debug_snapshot; + + use super::*; + + #[test] + fn test_mdd_stats() { + static FIXTURE: &str = include_str!("../fixtures/mdd.txt"); + + let result = many::, _, _>(parse()) + .easy_parse(FIXTURE) + .map_err(|err| err.map_position(|p| p.translate_position(FIXTURE))) + .unwrap(); + + assert_debug_snapshot!(result); + } +} diff --git a/src/parser.rs b/src/parser.rs index 05c0cea..0d8175e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. use crate::{ - ldlm, llite, + ldlm, llite, mdd_parser, mds::{self, client_count_parser}, mgs::mgs_parser, osd_parser, oss, top_level_parser, @@ -21,6 +21,7 @@ pub fn params() -> Vec { .chain(mds::params()) .chain(ldlm::params()) .chain(llite::params()) + .chain(mdd_parser::params()) .collect() } @@ -38,6 +39,7 @@ where mds::parse().map(|x| vec![x]), ldlm::parse().map(|x| vec![x]), llite::parse().map(|x| vec![x]), + mdd_parser::parse().map(|x| vec![x]), ))) .map(|xs: Vec<_>| xs.into_iter().flatten().collect()) } diff --git a/src/snapshots/lustre_collector__mdd_parser__tests__mdd_stats.snap b/src/snapshots/lustre_collector__mdd_parser__tests__mdd_stats.snap new file mode 100644 index 0000000..8e77bba --- /dev/null +++ b/src/snapshots/lustre_collector__mdd_parser__tests__mdd_stats.snap @@ -0,0 +1,49 @@ +--- +source: src/mdd_parser.rs +expression: result +--- +( + [ + Target( + Changelog( + TargetStat { + kind: Mdt, + param: Param( + "changelog_users", + ), + target: Target( + "ai400x2-MDT0000", + ), + value: ChangelogStat { + current_index: 0, + users: [ + ChangeLogUser { + user: "cl1", + index: 0, + idle_secs: 327, + }, + ], + }, + }, + ), + ), + Target( + Changelog( + TargetStat { + kind: Mdt, + param: Param( + "changelog_users", + ), + target: Target( + "ai400x2-MDT0001", + ), + value: ChangelogStat { + current_index: 0, + users: [], + }, + }, + ), + ), + ], + "", +) diff --git a/src/snapshots/lustre_collector__parser__tests__params.snap b/src/snapshots/lustre_collector__parser__tests__params.snap index 25d4735..55d4752 100644 --- a/src/snapshots/lustre_collector__parser__tests__params.snap +++ b/src/snapshots/lustre_collector__parser__tests__params.snap @@ -59,4 +59,5 @@ expression: params() "ldlm.services.ldlm_canceld.stats", "ldlm.services.ldlm_cbd.stats", "llite.*.stats", + "mdd.*.changelog_users", ] diff --git a/src/snapshots/lustre_collector__parser__tests__valid_fixture_valid.txt.snap b/src/snapshots/lustre_collector__parser__tests__valid_fixture_valid.txt.snap index 448d920..b0a2f7e 100644 --- a/src/snapshots/lustre_collector__parser__tests__valid_fixture_valid.txt.snap +++ b/src/snapshots/lustre_collector__parser__tests__valid_fixture_valid.txt.snap @@ -2629,6 +2629,46 @@ expression: result ], ), ), + Target( + Changelog( + TargetStat { + kind: Mdt, + param: Param( + "changelog_users", + ), + target: Target( + "ai400x2-MDT0000", + ), + value: ChangelogStat { + current_index: 0, + users: [ + ChangeLogUser { + user: "cl1", + index: 0, + idle_secs: 327, + }, + ], + }, + }, + ), + ), + Target( + Changelog( + TargetStat { + kind: Mdt, + param: Param( + "changelog_users", + ), + target: Target( + "ai400x2-MDT0001", + ), + value: ChangelogStat { + current_index: 0, + users: [], + }, + }, + ), + ), ], "", ) diff --git a/src/snapshots/lustre_collector__tests__params.snap b/src/snapshots/lustre_collector__tests__params.snap index 769a11d..11b4332 100644 --- a/src/snapshots/lustre_collector__tests__params.snap +++ b/src/snapshots/lustre_collector__tests__params.snap @@ -2,4 +2,4 @@ source: src/lib.rs expression: "xs.join(\" \")" --- -memused memused_max lnet_memused health_check mdt.*.exports.*.uuid osd-*.*.filesfree osd-*.*.filestotal osd-*.*.fstype osd-*.*.kbytesavail osd-*.*.kbytesfree osd-*.*.kbytestotal osd-*.*.brw_stats mgs.*.mgs.stats mgs.*.mgs.threads_max mgs.*.mgs.threads_min mgs.*.mgs.threads_started mgs.*.num_exports obdfilter.*OST*.job_stats obdfilter.*OST*.stats obdfilter.*OST*.num_exports obdfilter.*OST*.tot_dirty obdfilter.*OST*.tot_granted obdfilter.*OST*.tot_pending obdfilter.*OST*.exports.*.stats ost.OSS.ost.stats ost.OSS.ost_io.stats ost.OSS.ost_create.stats ost.OSS.ost_out.stats ost.OSS.ost_seq.stats mds.MDS.mdt.stats mds.MDS.mdt_fld.stats mds.MDS.mdt_io.stats mds.MDS.mdt_out.stats mds.MDS.mdt_readpage.stats mds.MDS.mdt_seqm.stats mds.MDS.mdt_seqs.stats mds.MDS.mdt_setattr.stats mdt.*.job_stats mdt.*.md_stats mdt.*MDT*.num_exports mdt.*MDT*.exports.*.stats ldlm.namespaces.{mdt-,filter-}*.contended_locks ldlm.namespaces.{mdt-,filter-}*.contention_seconds ldlm.namespaces.{mdt-,filter-}*.ctime_age_limit ldlm.namespaces.{mdt-,filter-}*.early_lock_cancel ldlm.namespaces.{mdt-,filter-}*.lock_count ldlm.namespaces.{mdt-,filter-}*.lock_timeouts ldlm.namespaces.{mdt-,filter-}*.lock_unused_count ldlm.namespaces.{mdt-,filter-}*.lru_max_age ldlm.namespaces.{mdt-,filter-}*.lru_size ldlm.namespaces.{mdt-,filter-}*.max_nolock_bytes ldlm.namespaces.{mdt-,filter-}*.max_parallel_ast ldlm.namespaces.{mdt-,filter-}*.resource_count ldlm.services.ldlm_canceld.stats ldlm.services.ldlm_cbd.stats llite.*.stats +memused memused_max lnet_memused health_check mdt.*.exports.*.uuid osd-*.*.filesfree osd-*.*.filestotal osd-*.*.fstype osd-*.*.kbytesavail osd-*.*.kbytesfree osd-*.*.kbytestotal osd-*.*.brw_stats mgs.*.mgs.stats mgs.*.mgs.threads_max mgs.*.mgs.threads_min mgs.*.mgs.threads_started mgs.*.num_exports obdfilter.*OST*.job_stats obdfilter.*OST*.stats obdfilter.*OST*.num_exports obdfilter.*OST*.tot_dirty obdfilter.*OST*.tot_granted obdfilter.*OST*.tot_pending obdfilter.*OST*.exports.*.stats ost.OSS.ost.stats ost.OSS.ost_io.stats ost.OSS.ost_create.stats ost.OSS.ost_out.stats ost.OSS.ost_seq.stats mds.MDS.mdt.stats mds.MDS.mdt_fld.stats mds.MDS.mdt_io.stats mds.MDS.mdt_out.stats mds.MDS.mdt_readpage.stats mds.MDS.mdt_seqm.stats mds.MDS.mdt_seqs.stats mds.MDS.mdt_setattr.stats mdt.*.job_stats mdt.*.md_stats mdt.*MDT*.num_exports mdt.*MDT*.exports.*.stats ldlm.namespaces.{mdt-,filter-}*.contended_locks ldlm.namespaces.{mdt-,filter-}*.contention_seconds ldlm.namespaces.{mdt-,filter-}*.ctime_age_limit ldlm.namespaces.{mdt-,filter-}*.early_lock_cancel ldlm.namespaces.{mdt-,filter-}*.lock_count ldlm.namespaces.{mdt-,filter-}*.lock_timeouts ldlm.namespaces.{mdt-,filter-}*.lock_unused_count ldlm.namespaces.{mdt-,filter-}*.lru_max_age ldlm.namespaces.{mdt-,filter-}*.lru_size ldlm.namespaces.{mdt-,filter-}*.max_nolock_bytes ldlm.namespaces.{mdt-,filter-}*.max_parallel_ast ldlm.namespaces.{mdt-,filter-}*.resource_count ldlm.services.ldlm_canceld.stats ldlm.services.ldlm_cbd.stats llite.*.stats mdd.*.changelog_users diff --git a/src/stats_parser.rs b/src/stats_parser.rs index 3a7c9aa..d0dae7e 100644 --- a/src/stats_parser.rs +++ b/src/stats_parser.rs @@ -6,6 +6,7 @@ use crate::{ base_parsers::{digits, not_words, word}, ldlm::LDLM, llite::LLITE, + mdd_parser::MDD, mds::mds_parser::MDS, oss::oss_parser::OST, time::time_triple, @@ -29,7 +30,7 @@ where I::Error: ParseError, { ( - not_words(&["obdfilter", "mgs", "mdt", LDLM, OST, LLITE, MDS]).skip(spaces()), + not_words(&["obdfilter", "mgs", "mdt", LDLM, OST, LLITE, MDS, MDD]).skip(spaces()), digits(), spaces().with(string("samples")), spaces().with(between(token('['), token(']'), word())), diff --git a/src/types.rs b/src/types.rs index a47b738..e863440 100644 --- a/src/types.rs +++ b/src/types.rs @@ -429,6 +429,20 @@ pub struct LNetStatGlobal { pub value: T, } +#[derive(PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)] +/// Changelog stats from parsing `mdd.*.changelog_users`. +pub struct ChangelogStat { + pub current_index: u64, + pub users: Vec, +} + +#[derive(PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)] +pub struct ChangeLogUser { + pub user: String, + pub index: u64, + pub idle_secs: u64, +} + impl TryFrom<&Target> for TargetVariant { type Error = LustreCollectorError; @@ -561,6 +575,7 @@ pub enum TargetStats { Llite(LliteStat), ExportStats(TargetStat>), Mds(MdsStat), + Changelog(TargetStat), } #[derive(PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]