Skip to content
This repository has been archived by the owner on May 21, 2024. It is now read-only.

Commit

Permalink
Implement qmt quota stats
Browse files Browse the repository at this point in the history
Implement osd quota stats

Fix test

Add fixtures

Add QuotaManager enum and removed "glb-" from qmt_stat

Fix typo in QMTStat::Prj assignement

Fix wording

Signed-off-by: Joe Grund <jgrund@whamcloud.io>
  • Loading branch information
jgrund committed May 8, 2024
1 parent 76653d7 commit 1492e02
Show file tree
Hide file tree
Showing 12 changed files with 8,463 additions and 7 deletions.
1,180 changes: 1,180 additions & 0 deletions src/fixtures/valid/2.14.0_ddn133_quota.txt

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod node_stats_parsers;
mod osd_parser;
mod oss;
pub mod parser;
pub(crate) mod quota;
pub mod recovery_status_parser;
mod stats_parser;
mod time;
Expand Down
43 changes: 43 additions & 0 deletions src/osd_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use crate::{
base_parsers::{digits, param, period, target, till_newline, till_period},
brw_stats_parser::brw_stats,
quota::quota_parser::quota_stats_osd,
types::{BrwStats, Param, Record, Target, TargetStat, TargetStats, TargetVariant},
QuotaKind, QuotaStatsOsd,
};
use combine::{
attempt, choice,
Expand All @@ -24,6 +26,10 @@ pub(crate) const FS_TYPE: &str = "fstype";

pub(crate) const BRW_STATS: &str = "brw_stats";

pub(crate) const QUOTA_ACCT_GRP: &str = "quota_slave.acct_group";
pub(crate) const QUOTA_ACCT_USR: &str = "quota_slave.acct_user";
pub(crate) const QUOTA_ACCT_PRJ: &str = "quota_slave.acct_project";

pub(crate) fn params() -> Vec<String> {
vec![
format!("osd-*.*.{FILES_FREE}"),
Expand All @@ -33,6 +39,9 @@ pub(crate) fn params() -> Vec<String> {
format!("osd-*.*.{KBYTES_FREE}"),
format!("osd-*.*.{KBYTES_TOTAL}"),
format!("osd-*.*.{BRW_STATS}"),
format!("osd-*.*.{QUOTA_ACCT_GRP}"),
format!("osd-*.*.{QUOTA_ACCT_USR}"),
format!("osd-*.*.{QUOTA_ACCT_PRJ}"),
]
}

Expand All @@ -51,6 +60,7 @@ enum OsdStat {
/// Total disk space
KBytesTotal(u64),
BrwStats(Vec<BrwStats>),
QuotaStats(QuotaStatsOsd),
}

fn target_and_variant<I>() -> impl Parser<I, Output = (Target, TargetVariant)>
Expand Down Expand Up @@ -107,6 +117,33 @@ where
param(KBYTES_TOTAL),
digits().skip(newline()).map(OsdStat::KBytesTotal),
),
(
param(QUOTA_ACCT_GRP),
quota_stats_osd().map(|stats| {
OsdStat::QuotaStats(QuotaStatsOsd {
kind: QuotaKind::Grp,
stats,
})
}),
),
(
param(QUOTA_ACCT_PRJ),
quota_stats_osd().map(|stats| {
OsdStat::QuotaStats(QuotaStatsOsd {
kind: QuotaKind::Prj,
stats,
})
}),
),
(
param(QUOTA_ACCT_USR),
quota_stats_osd().map(|stats| {
OsdStat::QuotaStats(QuotaStatsOsd {
kind: QuotaKind::Usr,
stats,
})
}),
),
))
}

Expand Down Expand Up @@ -159,6 +196,12 @@ where
param,
value,
}),
OsdStat::QuotaStats(value) => TargetStats::QuotaStatsOsd(TargetStat {
kind,
target,
param,
value,
}),
})
.map(Record::Target)
.message("while parsing osd")
Expand Down
7 changes: 4 additions & 3 deletions src/oss/job_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use crate::types::{JobStatOst, JobStatsOst};
use combine::{
attempt,
attempt, eof,
error::{ParseError, StreamError},
optional,
parser::{
Expand All @@ -22,9 +22,10 @@ where
{
(
optional(newline()), // If Jobstats are present, the whole yaml blob will be on a newline
take_until(attempt((newline(), alpha_num()))),
take_until(attempt((newline(), alpha_num()).map(drop).or(eof()))),
)
.skip(newline())
.skip(optional(newline()))
.skip(optional(eof()))
.and_then(|(_, x): (_, String)| {
serde_yaml::from_str(&x)
.map(|x: JobStatsOst| x.job_stats)
Expand Down
4 changes: 3 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
ldlm, llite, mdd_parser,
mds::{self, client_count_parser},
mgs::mgs_parser,
osd_parser, oss, top_level_parser,
osd_parser, oss, quota, top_level_parser,
types::Record,
};
use combine::{choice, error::ParseError, many, Parser, Stream};
Expand All @@ -22,6 +22,7 @@ pub fn params() -> Vec<String> {
.chain(ldlm::params())
.chain(llite::params())
.chain(mdd_parser::params())
.chain(quota::params())
.collect()
}

Expand All @@ -40,6 +41,7 @@ where
ldlm::parse().map(|x| vec![x]),
llite::parse().map(|x| vec![x]),
mdd_parser::parse().map(|x| vec![x]),
quota::parse().map(|x| vec![x]),
)))
.map(|xs: Vec<_>| xs.into_iter().flatten().collect())
}
Expand Down
22 changes: 22 additions & 0 deletions src/quota/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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::period, Record};
use combine::{parser::char::string, ParseError, Parser, Stream};

pub(crate) mod quota_parser;

pub(crate) const QMT: &str = "qmt";

pub(crate) fn params() -> Vec<String> {
quota_parser::params()
}

pub(crate) fn parse<I>() -> impl Parser<I, Output = Record>
where
I: Stream<Token = char>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
(string(QMT), period()).with(quota_parser::qmt_parse())
}
214 changes: 214 additions & 0 deletions src/quota/quota_parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// 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::{param, period, target},
quota::QMT,
types::{Param, Record, Target, TargetStats},
QuotaKind, QuotaStat, QuotaStatOsd, QuotaStats, TargetQuotaStat,
};
use combine::{
attempt, choice, eof,
error::{ParseError, StreamError},
many, one_of, optional,
parser::{
char::{alpha_num, newline, string},
repeat::take_until,
},
stream::{Stream, StreamErrorFor},
token, Parser,
};

pub(crate) const USR_QUOTAS: &str = "usr";
pub(crate) const PRJ_QUOTAS: &str = "prj";
pub(crate) const GRP_QUOTAS: &str = "grp";
pub(crate) const QMT_STATS: [&str; 3] = [USR_QUOTAS, PRJ_QUOTAS, GRP_QUOTAS];

/// Takes QMT_STATS and produces a list of params for
/// consumption in proper ltcl get_param format.
pub(crate) fn params() -> Vec<String> {
QMT_STATS
.iter()
.map(|x| format!("{QMT}.*.*.glb-{x}"))
.collect()
}

/// Parses a target name
pub(crate) fn qmt_pool<I>() -> impl Parser<I, Output = (Target, Target)>
where
I: Stream<Token = char>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
(
choice((
string("md").skip(token('-')).map(|x| Target(x.to_string())),
string("dt").skip(token('-')).map(|x| Target(x.to_string())),
)),
target(),
)
}

/// Parses the name of a target
fn qmt_target<I>() -> impl Parser<I, Output = (Target, Target, Target)>
where
I: Stream<Token = char>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
(target().skip(period()), qmt_pool().skip(period()))
.map(|(target, (manager, pool))| (target, manager, pool))
.message("while parsing target_name")
}

pub(crate) fn quota_stats<I>() -> impl Parser<I, Output = Vec<QuotaStat>>
where
I: Stream<Token = char>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
(
optional(newline()), // If quota stats are present, the whole yaml blob will start on a newline
many::<Vec<_>, _, _>(alpha_num().or(one_of("_-:".chars()))), // But yaml header might not be indented, ignore it
newline(),
take_until(attempt((newline(), alpha_num()).map(drop).or(eof()))),
)
.skip(optional(newline()))
.skip(optional(eof()))
.and_then(|(_, _, _, x): (_, _, _, String)| {
serde_yaml::from_str::<Vec<QuotaStat>>(&x).map_err(StreamErrorFor::<I>::other)
})
}

pub(crate) fn quota_stats_osd<I>() -> impl Parser<I, Output = Vec<QuotaStatOsd>>
where
I: Stream<Token = char>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
(
optional(newline()), // If quota stats are present, the whole yaml blob will start on a newline
many::<Vec<_>, _, _>(alpha_num().or(one_of("_-:".chars()))), // But yaml header might not be indented, ignore it
newline(),
take_until(attempt((newline(), alpha_num()).map(drop).or(eof()))),
)
.skip(optional(newline()))
.skip(optional(eof()))
.and_then(|(_, _, _, x): (_, _, _, String)| {
serde_yaml::from_str::<Vec<QuotaStatOsd>>(&x).map_err(StreamErrorFor::<I>::other)
})
}

#[derive(Debug)]
pub enum QMTStat {
Usr(Vec<QuotaStat>),
Prj(Vec<QuotaStat>),
Grp(Vec<QuotaStat>),
}

pub(crate) fn qmt_stat<I>() -> impl Parser<I, Output = (Param, QMTStat)>
where
I: Stream<Token = char>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
(
string("glb-"),
choice((
(param(USR_QUOTAS), quota_stats().map(QMTStat::Usr)),
(param(PRJ_QUOTAS), quota_stats().map(QMTStat::Prj)),
(param(GRP_QUOTAS), quota_stats().map(QMTStat::Grp)),
)),
)
.map(|(_, param)| (param))
}
pub(crate) fn qmt_parse<I>() -> impl Parser<I, Output = Record>
where
I: Stream<Token = char>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
(qmt_target(), qmt_stat())
.map(
|((target, Target(manager), Target(pool)), (param, value))| match value {
QMTStat::Usr(stats) => TargetStats::QuotaStats(TargetQuotaStat {
pool,
manager,
target,
param,
value: QuotaStats {
kind: QuotaKind::Usr,
stats,
},
}),
QMTStat::Prj(stats) => TargetStats::QuotaStats(TargetQuotaStat {
pool,
manager,
target,
param,
value: QuotaStats {
kind: QuotaKind::Prj,
stats,
},
}),
QMTStat::Grp(stats) => TargetStats::QuotaStats(TargetQuotaStat {
pool,
manager,
target,
param,
value: QuotaStats {
kind: QuotaKind::Grp,
stats,
},
}),
},
)
.map(Record::Target)
.message("while parsing qmt")
}

#[cfg(test)]
mod tests {
use crate::{QuotaStat, QuotaStatLimits};

use super::*;

#[test]
fn test_qmt_params() {
assert_eq!(
params(),
vec![
"qmt.*.*.glb-usr".to_string(),
"qmt.*.*.glb-prj".to_string(),
"qmt.*.*.glb-grp".to_string(),
]
)
}

#[test]
fn test_yaml_deserialize() {
let x = r#"
- id: 0
limits: { hard: 0, soft: 0, granted: 0, time: 604800 }
- id: 1337
limits: { hard: 309200, soft: 307200, granted: 1025032, time: 1687277628 }"#;

let expected = vec![
QuotaStat {
id: 0,
limits: QuotaStatLimits {
hard: 0,
soft: 0,
granted: 0,
time: 604800,
},
},
QuotaStat {
id: 1337,
limits: QuotaStatLimits {
hard: 309200,
soft: 307200,
granted: 1025032,
time: 1687277628,
},
},
];

assert_eq!(serde_yaml::from_str::<Vec<QuotaStat>>(x).unwrap(), expected)
}
}
6 changes: 6 additions & 0 deletions src/snapshots/lustre_collector__parser__tests__params.snap
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ expression: params()
"osd-*.*.kbytesfree",
"osd-*.*.kbytestotal",
"osd-*.*.brw_stats",
"osd-*.*.quota_slave.acct_group",
"osd-*.*.quota_slave.acct_user",
"osd-*.*.quota_slave.acct_project",
"mgs.*.mgs.stats",
"mgs.*.mgs.threads_max",
"mgs.*.mgs.threads_min",
Expand Down Expand Up @@ -60,4 +63,7 @@ expression: params()
"ldlm.services.ldlm_cbd.stats",
"llite.*.stats",
"mdd.*.changelog_users",
"qmt.*.*.glb-usr",
"qmt.*.*.glb-prj",
"qmt.*.*.glb-grp",
]
Loading

0 comments on commit 1492e02

Please sign in to comment.