1use std::time::Duration;
2
3use serde::Deserialize;
4use serde::Serialize;
5use tc::QDisc;
6use tc::TcStat;
7use tc::TcStats;
8use tc::XStats;
9
10use crate::Field;
11use crate::FieldId;
12use crate::Nameable;
13use crate::Queriable;
14
15macro_rules! rate {
18 ($field:ident, $sample:ident, $last:ident, $target_type:ty) => {{
19 $last.and_then(|(last, d)| {
20 let s = $sample.$field;
21 let l = last.$field;
22 count_per_sec!(l, s, d, $target_type)
23 })
24 }};
25}
26
27#[below_derive::queriable_derives]
28pub struct TcModel {
29 #[queriable(subquery)]
30 pub tc: Vec<SingleTcModel>,
31}
32
33impl TcModel {
34 pub fn new(sample: &TcStats, last: Option<(&TcStats, Duration)>) -> Self {
35 let tc = match last {
37 Some((last_tcs, d)) if last_tcs.len() == sample.len() => sample
38 .iter()
39 .zip(last_tcs.iter())
40 .map(|(sample, last)| SingleTcModel::new(sample, Some((last, d))))
41 .collect::<Vec<_>>(),
42 _ => Vec::new(),
43 };
44
45 Self { tc }
46 }
47}
48
49#[below_derive::queriable_derives]
50pub struct SingleTcModel {
51 pub interface: String,
53 pub kind: String,
55
56 pub qlen: Option<u32>,
57 pub bps: Option<u32>,
58 pub pps: Option<u32>,
59
60 pub bytes_per_sec: Option<u64>,
61 pub packets_per_sec: Option<u32>,
62 pub backlog_per_sec: Option<u32>,
63 pub drops_per_sec: Option<u32>,
64 pub requeues_per_sec: Option<u32>,
65 pub overlimits_per_sec: Option<u32>,
66
67 #[queriable(subquery)]
68 pub qdisc: Option<QDiscModel>,
69
70 #[queriable(subquery)]
71 pub xstats: Option<XStatsModel>,
72}
73
74impl Nameable for SingleTcModel {
75 fn name() -> &'static str {
76 "tc"
77 }
78}
79
80impl SingleTcModel {
81 pub fn new(sample: &TcStat, last: Option<(&TcStat, Duration)>) -> Self {
82 let mut tc_model = SingleTcModel {
83 interface: sample.if_name.clone(),
84 kind: sample.kind.clone(),
85 ..Default::default()
86 };
87
88 let stats = &sample.stats;
89 tc_model.qlen = stats.qlen;
90 tc_model.bps = stats.bps;
91 tc_model.pps = stats.pps;
92
93 if let Some((l, d)) = last {
94 let last = &l.stats;
95 tc_model.bytes_per_sec = count_per_sec!(last.bytes, stats.bytes, d, u64);
96 tc_model.packets_per_sec = count_per_sec!(last.packets, stats.packets, d, u32);
97 tc_model.backlog_per_sec = count_per_sec!(last.backlog, stats.backlog, d, u32);
98 tc_model.drops_per_sec = count_per_sec!(last.drops, stats.drops, d, u32);
99 tc_model.requeues_per_sec = count_per_sec!(last.requeues, stats.requeues, d, u32);
100 tc_model.overlimits_per_sec = count_per_sec!(last.overlimits, stats.overlimits, d, u32);
101 }
102
103 if let Some(sample) = stats.xstats.as_ref() {
104 let last = last.and_then(|(last, d)| last.stats.xstats.as_ref().map(|l| (l, d)));
105
106 tc_model.xstats = XStatsModel::new(sample, last);
107 }
108
109 if let Some(sample) = sample.qdisc.as_ref() {
110 tc_model.qdisc = Some(QDiscModel::new(sample));
111 }
112
113 tc_model
114 }
115}
116
117#[below_derive::queriable_derives]
118pub struct QDiscModel {
119 #[queriable(subquery)]
120 pub fq_codel: Option<FqCodelQDiscModel>,
121}
122
123impl QDiscModel {
124 fn new(sample: &QDisc) -> Self {
125 match sample {
126 QDisc::FqCodel(sample) => Self {
127 fq_codel: Some(FqCodelQDiscModel::new(sample)),
128 },
129 }
130 }
131}
132
133#[below_derive::queriable_derives]
134pub struct FqCodelQDiscModel {
135 pub target: u32,
136 pub limit: u32,
137 pub interval: u32,
138 pub ecn: u32,
139 pub quantum: u32,
140 pub ce_threshold: u32,
141 pub drop_batch_size: u32,
142 pub memory_limit: u32,
143 pub flows: u32,
144}
145
146impl FqCodelQDiscModel {
147 fn new(sample: &tc::FqCodelQDisc) -> Self {
148 Self {
149 target: sample.target,
150 limit: sample.limit,
151 interval: sample.interval,
152 ecn: sample.ecn,
153 quantum: sample.quantum,
154 ce_threshold: sample.ce_threshold,
155 drop_batch_size: sample.drop_batch_size,
156 memory_limit: sample.memory_limit,
157 flows: sample.flows,
158 }
159 }
160}
161
162#[derive(
163 Clone,
164 Debug,
165 Default,
166 PartialEq,
167 Serialize,
168 Deserialize,
169 below_derive::Queriable
170)]
171pub struct XStatsModel {
172 #[queriable(subquery)]
173 pub fq_codel: Option<FqCodelXStatsModel>,
174}
175
176impl XStatsModel {
177 fn new(sample: &XStats, last: Option<(&XStats, Duration)>) -> Option<Self> {
178 match (sample, last) {
179 (XStats::FqCodel(sample), Some((XStats::FqCodel(last), d))) => match (sample, last) {
180 (
181 tc::FqCodelXStats::FqCodelQdiscStats(sample),
182 tc::FqCodelXStats::FqCodelQdiscStats(last),
183 ) => Some(Self {
184 fq_codel: Some(FqCodelXStatsModel::new(sample, Some((last, d)))),
185 }),
186 },
187 _ => None,
188 }
189 }
190}
191
192#[derive(
193 Clone,
194 Debug,
195 Default,
196 PartialEq,
197 Serialize,
198 Deserialize,
199 below_derive::Queriable
200)]
201pub struct FqCodelXStatsModel {
202 pub maxpacket: u32,
204 pub ecn_mark: u32,
205 pub new_flows_len: u32,
206 pub old_flows_len: u32,
207 pub ce_mark: u32,
208 pub drop_overlimit_per_sec: Option<u32>,
209 pub new_flow_count_per_sec: Option<u32>,
210 pub memory_usage_per_sec: Option<u32>,
211 pub drop_overmemory_per_sec: Option<u32>,
212}
213
214impl FqCodelXStatsModel {
215 fn new(sample: &tc::FqCodelQdStats, last: Option<(&tc::FqCodelQdStats, Duration)>) -> Self {
216 Self {
217 maxpacket: sample.maxpacket,
218 ecn_mark: sample.ecn_mark,
219 new_flows_len: sample.new_flows_len,
220 old_flows_len: sample.old_flows_len,
221 ce_mark: sample.ce_mark,
222 drop_overlimit_per_sec: rate!(drop_overlimit, sample, last, u32),
223 new_flow_count_per_sec: rate!(drop_overlimit, sample, last, u32),
224 memory_usage_per_sec: rate!(drop_overlimit, sample, last, u32),
225 drop_overmemory_per_sec: rate!(drop_overlimit, sample, last, u32),
226 }
227 }
228}