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

Commit

Permalink
Implement quota stats (#58)
Browse files Browse the repository at this point in the history
* Implement qmt quota stats

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>

* Take until newline

Signed-off-by: Joe Grund <jgrund@whamcloud.io>

---------

Signed-off-by: Joe Grund <jgrund@whamcloud.io>
Co-authored-by: Joe Grund <jgrund@whamcloud.io>
  • Loading branch information
RDruon and jgrund authored May 8, 2024
1 parent 76653d7 commit 3f166c3
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},
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
take_until::<Vec<_>, _, _>(newline()), // 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
take_until::<Vec<_>, _, _>(newline()), // 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 3f166c3

Please sign in to comment.