1use super::*;
16
17#[::below_derive::queriable_derives]
18pub struct NetworkModel {
19 #[queriable(subquery)]
20 pub interfaces: BTreeMap<String, SingleNetModel>,
21 #[queriable(subquery)]
22 pub tcp: TcpModel,
23 #[queriable(subquery)]
24 pub ip: IpModel,
25 #[queriable(subquery)]
26 pub ip6: Ip6Model,
27 #[queriable(subquery)]
28 pub icmp: IcmpModel,
29 #[queriable(subquery)]
30 pub icmp6: Icmp6Model,
31 #[queriable(subquery)]
32 pub udp: UdpModel,
33 #[queriable(subquery)]
34 pub udp6: Udp6Model,
35}
36
37impl NetworkModel {
38 pub fn new(sample: &NetworkStats, last: Option<(&NetworkStats, Duration)>) -> Self {
39 let mut interfaces: BTreeMap<String, SingleNetModel> = BTreeMap::new();
40
41 let net_stats = sample.net;
42 let ethtool_stats = sample.ethtool;
43
44 let mut iface_names = BTreeSet::new();
45 if let Some(ifaces) = net_stats.interfaces.as_ref() {
46 for (interface, _) in ifaces.iter() {
47 iface_names.insert(interface.to_string());
48 }
49 }
50 if let Some(ethtool_stats) = ethtool_stats {
51 for key in ethtool_stats.nic.keys() {
52 iface_names.insert(key.to_string());
53 }
54 }
55
56 for interface in iface_names {
57 let iface_stat = net_stats
58 .interfaces
59 .as_ref()
60 .and_then(|ifaces| ifaces.get(&interface));
61 let ethtool_stat = ethtool_stats
62 .as_ref()
63 .and_then(|stat| stat.nic.get(&interface));
64
65 let s_iface = SingleNetworkStat {
66 iface: iface_stat,
67 nic: ethtool_stat,
68 };
69
70 let mut l_network_stat = SingleNetworkStat {
71 iface: None,
72 nic: None,
73 };
74 let l_iface = last.map(|(l, d)| {
75 let l_iface_stat = l
76 .net
77 .interfaces
78 .as_ref()
79 .and_then(|ifaces| ifaces.get(&interface));
80 let l_ethtool_stat = l.ethtool.as_ref().and_then(|stat| stat.nic.get(&interface));
81 l_network_stat = SingleNetworkStat {
82 iface: l_iface_stat,
83 nic: l_ethtool_stat,
84 };
85 (&l_network_stat, d)
86 });
87
88 let net_model = SingleNetModel::new(&interface, &s_iface, l_iface);
89 interfaces.insert(interface, net_model);
90 }
91
92 NetworkModel {
93 interfaces,
94 tcp: TcpModel::new(
95 sample.net.tcp.as_ref().unwrap_or(&Default::default()),
96 last.and_then(|(l, d)| {
97 let n = l.net;
98 n.tcp.as_ref().map(|n| (n, d))
99 }),
100 ),
101 ip: IpModel::new(
102 sample.net.ip.as_ref().unwrap_or(&Default::default()),
103 last.and_then(|(l, d)| {
104 let n = l.net;
105 n.ip.as_ref().map(|n| (n, d))
106 }),
107 sample.net.ip_ext.as_ref().unwrap_or(&Default::default()),
108 last.and_then(|(l, d)| {
109 let n = l.net;
110 n.ip_ext.as_ref().map(|n| (n, d))
111 }),
112 ),
113 ip6: Ip6Model::new(
114 sample.net.ip6.as_ref().unwrap_or(&Default::default()),
115 last.and_then(|(l, d)| {
116 let n = l.net;
117 n.ip6.as_ref().map(|n| (n, d))
118 }),
119 ),
120 icmp: IcmpModel::new(
121 sample.net.icmp.as_ref().unwrap_or(&Default::default()),
122 last.and_then(|(l, d)| {
123 let n = l.net;
124 n.icmp.as_ref().map(|n| (n, d))
125 }),
126 ),
127 icmp6: Icmp6Model::new(
128 sample.net.icmp6.as_ref().unwrap_or(&Default::default()),
129 last.and_then(|(l, d)| {
130 let n = l.net;
131 n.icmp6.as_ref().map(|n| (n, d))
132 }),
133 ),
134 udp: UdpModel::new(
135 sample.net.udp.as_ref().unwrap_or(&Default::default()),
136 last.and_then(|(l, d)| {
137 let n = l.net;
138 n.udp.as_ref().map(|n| (n, d))
139 }),
140 ),
141 udp6: Udp6Model::new(
142 sample.net.udp6.as_ref().unwrap_or(&Default::default()),
143 last.and_then(|(l, d)| {
144 let n = l.net;
145 n.udp6.as_ref().map(|n| (n, d))
146 }),
147 ),
148 }
149 }
150}
151
152impl Nameable for NetworkModel {
153 fn name() -> &'static str {
154 "network"
155 }
156}
157
158#[::below_derive::queriable_derives]
159pub struct TcpModel {
160 pub active_opens_per_sec: Option<u64>,
161 pub passive_opens_per_sec: Option<u64>,
162 pub attempt_fails_per_sec: Option<u64>,
163 pub estab_resets_per_sec: Option<u64>,
164 pub curr_estab_conn: Option<u64>,
165 pub in_segs_per_sec: Option<u64>,
166 pub out_segs_per_sec: Option<u64>,
167 pub retrans_segs_per_sec: Option<u64>,
168 pub retrans_segs: Option<u64>,
169 pub in_errs: Option<u64>,
170 pub out_rsts_per_sec: Option<u64>,
171 pub in_csum_errors: Option<u64>,
172 }
175
176impl TcpModel {
177 pub fn new(sample: &procfs::TcpStat, last: Option<(&procfs::TcpStat, Duration)>) -> TcpModel {
178 TcpModel {
179 active_opens_per_sec: get_option_rate!(active_opens, sample, last),
180 passive_opens_per_sec: get_option_rate!(passive_opens, sample, last),
181 attempt_fails_per_sec: get_option_rate!(attempt_fails, sample, last),
182 estab_resets_per_sec: get_option_rate!(estab_resets, sample, last),
183 curr_estab_conn: sample.curr_estab,
184 in_segs_per_sec: get_option_rate!(in_segs, sample, last),
185 out_segs_per_sec: get_option_rate!(out_segs, sample, last),
186 retrans_segs_per_sec: get_option_rate!(retrans_segs, sample, last),
187 retrans_segs: sample.retrans_segs,
188 in_errs: sample.in_errs,
189 out_rsts_per_sec: get_option_rate!(out_rsts, sample, last),
190 in_csum_errors: sample.in_csum_errors,
191 }
192 }
193}
194
195#[::below_derive::queriable_derives]
196pub struct IpModel {
197 pub forwarding_pkts_per_sec: Option<u64>,
198 pub in_receives_pkts_per_sec: Option<u64>,
199 pub forw_datagrams_per_sec: Option<u64>,
200 pub in_discards_pkts_per_sec: Option<u64>,
201 pub in_delivers_pkts_per_sec: Option<u64>,
202 pub out_requests_per_sec: Option<u64>,
203 pub out_discards_pkts_per_sec: Option<u64>,
204 pub out_no_routes_pkts_per_sec: Option<u64>,
205 pub in_mcast_pkts_per_sec: Option<u64>,
207 pub out_mcast_pkts_per_sec: Option<u64>,
208 pub in_bcast_pkts_per_sec: Option<u64>,
209 pub out_bcast_pkts_per_sec: Option<u64>,
210 pub in_octets_per_sec: Option<u64>,
211 pub out_octets_per_sec: Option<u64>,
212 pub in_mcast_octets_per_sec: Option<u64>,
213 pub out_mcast_octets_per_sec: Option<u64>,
214 pub in_bcast_octets_per_sec: Option<u64>,
215 pub out_bcast_octets_per_sec: Option<u64>,
216 pub in_no_ect_pkts_per_sec: Option<u64>,
217}
218
219impl IpModel {
220 pub fn new(
221 sample: &procfs::IpStat,
222 last: Option<(&procfs::IpStat, Duration)>,
223 sample_ext: &procfs::IpExtStat,
224 last_ext: Option<(&procfs::IpExtStat, Duration)>,
225 ) -> IpModel {
226 IpModel {
227 forwarding_pkts_per_sec: get_option_rate!(forwarding, sample, last),
228 in_receives_pkts_per_sec: get_option_rate!(in_receives, sample, last),
229 forw_datagrams_per_sec: get_option_rate!(forw_datagrams, sample, last),
230 in_discards_pkts_per_sec: get_option_rate!(in_discards, sample, last),
231 in_delivers_pkts_per_sec: get_option_rate!(in_delivers, sample, last),
232 out_requests_per_sec: get_option_rate!(out_requests, sample, last),
233 out_discards_pkts_per_sec: get_option_rate!(out_discards, sample, last),
234 out_no_routes_pkts_per_sec: get_option_rate!(out_no_routes, sample, last),
235 in_mcast_pkts_per_sec: get_option_rate!(in_mcast_pkts, sample_ext, last_ext),
237 out_mcast_pkts_per_sec: get_option_rate!(out_mcast_pkts, sample_ext, last_ext),
238 in_bcast_pkts_per_sec: get_option_rate!(in_bcast_pkts, sample_ext, last_ext),
239 out_bcast_pkts_per_sec: get_option_rate!(out_bcast_pkts, sample_ext, last_ext),
240 in_octets_per_sec: get_option_rate!(in_octets, sample_ext, last_ext),
241 out_octets_per_sec: get_option_rate!(out_octets, sample_ext, last_ext),
242 in_mcast_octets_per_sec: get_option_rate!(in_mcast_octets, sample_ext, last_ext),
243 out_mcast_octets_per_sec: get_option_rate!(out_mcast_octets, sample_ext, last_ext),
244 in_bcast_octets_per_sec: get_option_rate!(in_bcast_octets, sample_ext, last_ext),
245 out_bcast_octets_per_sec: get_option_rate!(out_bcast_octets, sample_ext, last_ext),
246 in_no_ect_pkts_per_sec: get_option_rate!(in_no_ect_pkts, sample_ext, last_ext),
247 }
248 }
249}
250
251#[::below_derive::queriable_derives]
252pub struct Ip6Model {
253 pub in_receives_pkts_per_sec: Option<u64>,
254 pub in_hdr_errors: Option<u64>,
255 pub in_no_routes_pkts_per_sec: Option<u64>,
256 pub in_addr_errors: Option<u64>,
257 pub in_discards_pkts_per_sec: Option<u64>,
258 pub in_delivers_pkts_per_sec: Option<u64>,
259 pub out_forw_datagrams_per_sec: Option<u64>,
260 pub out_requests_per_sec: Option<u64>,
261 pub out_no_routes_pkts_per_sec: Option<u64>,
262 pub in_mcast_pkts_per_sec: Option<u64>,
263 pub out_mcast_pkts_per_sec: Option<u64>,
264 pub in_octets_per_sec: Option<u64>,
265 pub out_octets_per_sec: Option<u64>,
266 pub in_mcast_octets_per_sec: Option<u64>,
267 pub out_mcast_octets_per_sec: Option<u64>,
268 pub in_bcast_octets_per_sec: Option<u64>,
269 pub out_bcast_octets_per_sec: Option<u64>,
270}
271
272impl Ip6Model {
273 pub fn new(sample: &procfs::Ip6Stat, last: Option<(&procfs::Ip6Stat, Duration)>) -> Ip6Model {
274 Ip6Model {
275 in_receives_pkts_per_sec: get_option_rate!(in_receives, sample, last),
276 in_hdr_errors: sample.in_hdr_errors,
277 in_no_routes_pkts_per_sec: get_option_rate!(in_no_routes, sample, last),
278 in_addr_errors: sample.in_addr_errors,
279 in_discards_pkts_per_sec: get_option_rate!(in_discards, sample, last),
280 in_delivers_pkts_per_sec: get_option_rate!(in_delivers, sample, last),
281 out_forw_datagrams_per_sec: get_option_rate!(out_forw_datagrams, sample, last),
282 out_requests_per_sec: get_option_rate!(out_requests, sample, last),
283 out_no_routes_pkts_per_sec: get_option_rate!(out_no_routes, sample, last),
284 in_mcast_pkts_per_sec: get_option_rate!(in_mcast_pkts, sample, last),
285 out_mcast_pkts_per_sec: get_option_rate!(out_mcast_pkts, sample, last),
286 in_octets_per_sec: get_option_rate!(in_octets, sample, last),
287 out_octets_per_sec: get_option_rate!(out_octets, sample, last),
288 in_mcast_octets_per_sec: get_option_rate!(in_mcast_octets, sample, last),
289 out_mcast_octets_per_sec: get_option_rate!(out_mcast_octets, sample, last),
290 in_bcast_octets_per_sec: get_option_rate!(in_bcast_octets, sample, last),
291 out_bcast_octets_per_sec: get_option_rate!(out_bcast_octets, sample, last),
292 }
293 }
294}
295
296#[::below_derive::queriable_derives]
297pub struct IcmpModel {
298 pub in_msgs_per_sec: Option<u64>,
299 pub in_errors: Option<u64>,
300 pub in_dest_unreachs: Option<u64>,
301 pub out_msgs_per_sec: Option<u64>,
302 pub out_errors: Option<u64>,
303 pub out_dest_unreachs: Option<u64>,
304}
305
306impl IcmpModel {
307 pub fn new(
308 sample: &procfs::IcmpStat,
309 last: Option<(&procfs::IcmpStat, Duration)>,
310 ) -> IcmpModel {
311 IcmpModel {
312 in_msgs_per_sec: get_option_rate!(in_msgs, sample, last),
313 in_errors: sample.in_errors,
314 in_dest_unreachs: sample.in_dest_unreachs,
315 out_msgs_per_sec: get_option_rate!(out_msgs, sample, last),
316 out_errors: sample.out_errors,
317 out_dest_unreachs: sample.out_dest_unreachs,
318 }
319 }
320}
321
322#[::below_derive::queriable_derives]
323pub struct Icmp6Model {
324 pub in_msgs_per_sec: Option<u64>,
325 pub in_errors: Option<u64>,
326 pub in_dest_unreachs: Option<u64>,
327 pub out_msgs_per_sec: Option<u64>,
328 pub out_errors: Option<u64>,
329 pub out_dest_unreachs: Option<u64>,
330}
331
332impl Icmp6Model {
333 pub fn new(
334 sample: &procfs::Icmp6Stat,
335 last: Option<(&procfs::Icmp6Stat, Duration)>,
336 ) -> Icmp6Model {
337 Icmp6Model {
338 in_msgs_per_sec: get_option_rate!(in_msgs, sample, last),
339 in_errors: sample.in_errors,
340 in_dest_unreachs: sample.in_dest_unreachs,
341 out_msgs_per_sec: get_option_rate!(out_msgs, sample, last),
342 out_errors: sample.out_errors,
343 out_dest_unreachs: sample.out_dest_unreachs,
344 }
345 }
346}
347
348#[::below_derive::queriable_derives]
349pub struct UdpModel {
350 pub in_datagrams_pkts_per_sec: Option<u64>,
351 pub no_ports: Option<u64>,
352 pub in_errors: Option<u64>,
353 pub out_datagrams_pkts_per_sec: Option<u64>,
354 pub rcvbuf_errors: Option<u64>,
355 pub sndbuf_errors: Option<u64>,
356 pub ignored_multi: Option<u64>,
357}
358
359impl UdpModel {
360 pub fn new(sample: &procfs::UdpStat, last: Option<(&procfs::UdpStat, Duration)>) -> UdpModel {
361 UdpModel {
362 in_datagrams_pkts_per_sec: get_option_rate!(in_datagrams, sample, last),
363 no_ports: sample.no_ports,
364 in_errors: sample.in_errors,
365 out_datagrams_pkts_per_sec: get_option_rate!(out_datagrams, sample, last),
366 rcvbuf_errors: sample.rcvbuf_errors,
367 sndbuf_errors: sample.sndbuf_errors,
368 ignored_multi: sample.ignored_multi,
369 }
370 }
371}
372
373#[::below_derive::queriable_derives]
374pub struct Udp6Model {
375 pub in_datagrams_pkts_per_sec: Option<u64>,
376 pub no_ports: Option<u64>,
377 pub in_errors: Option<u64>,
378 pub out_datagrams_pkts_per_sec: Option<u64>,
379 pub rcvbuf_errors: Option<u64>,
380 pub sndbuf_errors: Option<u64>,
381 pub in_csum_errors: Option<u64>,
382 pub ignored_multi: Option<u64>,
383}
384
385impl Udp6Model {
386 pub fn new(
387 sample: &procfs::Udp6Stat,
388 last: Option<(&procfs::Udp6Stat, Duration)>,
389 ) -> Udp6Model {
390 Udp6Model {
391 in_datagrams_pkts_per_sec: get_option_rate!(in_datagrams, sample, last),
392 no_ports: sample.no_ports,
393 in_errors: sample.in_errors,
394 out_datagrams_pkts_per_sec: get_option_rate!(out_datagrams, sample, last),
395 rcvbuf_errors: sample.rcvbuf_errors,
396 sndbuf_errors: sample.sndbuf_errors,
397 in_csum_errors: sample.in_csum_errors,
398 ignored_multi: sample.ignored_multi,
399 }
400 }
401}
402
403#[::below_derive::queriable_derives]
404pub struct SingleNetModel {
405 pub interface: String,
406 pub rx_bytes_per_sec: Option<f64>,
407 pub tx_bytes_per_sec: Option<f64>,
408 pub throughput_per_sec: Option<f64>,
409 pub rx_packets_per_sec: Option<u64>,
410 pub tx_packets_per_sec: Option<u64>,
411 pub collisions: Option<u64>,
412 pub multicast: Option<u64>,
413 pub rx_bytes: Option<u64>,
414 pub rx_compressed: Option<u64>,
415 pub rx_crc_errors: Option<u64>,
416 pub rx_dropped: Option<u64>,
417 pub rx_errors: Option<u64>,
418 pub rx_fifo_errors: Option<u64>,
419 pub rx_frame_errors: Option<u64>,
420 pub rx_length_errors: Option<u64>,
421 pub rx_missed_errors: Option<u64>,
422 pub rx_nohandler: Option<u64>,
423 pub rx_over_errors: Option<u64>,
424 pub rx_packets: Option<u64>,
425 pub tx_aborted_errors: Option<u64>,
426 pub tx_bytes: Option<u64>,
427 pub tx_carrier_errors: Option<u64>,
428 pub tx_compressed: Option<u64>,
429 pub tx_dropped: Option<u64>,
430 pub tx_errors: Option<u64>,
431 pub tx_fifo_errors: Option<u64>,
432 pub tx_heartbeat_errors: Option<u64>,
433 pub tx_packets: Option<u64>,
434 pub tx_window_errors: Option<u64>,
435 pub tx_timeout_per_sec: Option<u64>,
436 pub raw_stats: BTreeMap<String, u64>,
437
438 #[queriable(subquery)]
439 pub queues: Vec<SingleQueueModel>,
440}
441
442pub struct SingleNetworkStat<'a> {
443 iface: Option<&'a procfs::InterfaceStat>,
444 nic: Option<&'a ethtool::NicStats>,
445}
446
447impl SingleNetModel {
448 fn add_iface_stats(
449 net_model: &mut SingleNetModel,
450 sample: &procfs::InterfaceStat,
451 last: Option<(&procfs::InterfaceStat, Duration)>,
452 ) {
453 let rx_bytes_per_sec = last
454 .map(|(l, d)| count_per_sec!(l.rx_bytes, sample.rx_bytes, d))
455 .unwrap_or_default();
456 let tx_bytes_per_sec = last
457 .map(|(l, d)| count_per_sec!(l.tx_bytes, sample.tx_bytes, d))
458 .unwrap_or_default();
459 let throughput_per_sec =
460 Some(rx_bytes_per_sec.unwrap_or_default() + tx_bytes_per_sec.unwrap_or_default());
461
462 net_model.rx_bytes_per_sec = rx_bytes_per_sec;
463 net_model.tx_bytes_per_sec = tx_bytes_per_sec;
464 net_model.throughput_per_sec = throughput_per_sec;
465 net_model.rx_packets_per_sec = last
466 .map(|(l, d)| count_per_sec!(l.rx_packets, sample.rx_packets, d))
467 .unwrap_or_default()
468 .map(|s| s as u64);
469 net_model.tx_packets_per_sec = last
470 .map(|(l, d)| count_per_sec!(l.tx_packets, sample.tx_packets, d))
471 .unwrap_or_default()
472 .map(|s| s as u64);
473 net_model.collisions = sample.collisions;
474 net_model.multicast = sample.multicast;
475 net_model.rx_bytes = sample.rx_bytes;
476 net_model.rx_compressed = sample.rx_compressed;
477 net_model.rx_crc_errors = sample.rx_crc_errors;
478 net_model.rx_dropped = sample.rx_dropped;
479 net_model.rx_errors = sample.rx_errors;
480 net_model.rx_fifo_errors = sample.rx_fifo_errors;
481 net_model.rx_frame_errors = sample.rx_frame_errors;
482 net_model.rx_length_errors = sample.rx_length_errors;
483 net_model.rx_missed_errors = sample.rx_missed_errors;
484 net_model.rx_nohandler = sample.rx_nohandler;
485 net_model.rx_over_errors = sample.rx_over_errors;
486 net_model.rx_packets = sample.rx_packets;
487 net_model.tx_aborted_errors = sample.tx_aborted_errors;
488 net_model.tx_bytes = sample.tx_bytes;
489 net_model.tx_carrier_errors = sample.tx_carrier_errors;
490 net_model.tx_compressed = sample.tx_compressed;
491 net_model.tx_dropped = sample.tx_dropped;
492 net_model.tx_errors = sample.tx_errors;
493 net_model.tx_fifo_errors = sample.tx_fifo_errors;
494 net_model.tx_heartbeat_errors = sample.tx_heartbeat_errors;
495 net_model.tx_packets = sample.tx_packets;
496 net_model.tx_window_errors = sample.tx_window_errors;
497 }
498
499 fn add_ethtool_stats(
500 net_model: &mut SingleNetModel,
501 sample: ðtool::NicStats,
502 last: Option<(ðtool::NicStats, Duration)>,
503 ) {
504 net_model.tx_timeout_per_sec = get_option_rate!(tx_timeout, sample, last);
505 net_model.raw_stats = sample.raw_stats.clone();
506
507 let s_queue_stats = &sample.queue;
509 for (queue_id, s_queue_stats) in s_queue_stats.iter().enumerate() {
511 let idx = queue_id as u32;
512 let last = last.and_then(|(l, d)| {
513 let queue_stats = &l.queue;
514 queue_stats.get(queue_id).map(|n| (n, d))
515 });
516 let queue_model = SingleQueueModel::new(&net_model.interface, idx, s_queue_stats, last);
517 net_model.queues.push(queue_model);
518 }
519 }
520
521 fn new(
522 interface: &str,
523 sample: &SingleNetworkStat,
524 last: Option<(&SingleNetworkStat, Duration)>,
525 ) -> SingleNetModel {
526 let iface_stat = sample.iface;
527 let ethtool_stat = sample.nic;
528
529 let mut net_model = SingleNetModel {
530 interface: interface.to_string(),
531 ..Default::default()
532 };
533
534 if let Some(iface_stat) = iface_stat {
536 Self::add_iface_stats(
537 &mut net_model,
538 iface_stat,
539 last.and_then(|(l, d)| l.iface.map(|l| (l, d))),
540 );
541 }
542
543 if let Some(nic_stat) = ethtool_stat {
545 let sample = nic_stat;
546 let last = last.and_then(|(l, d)| l.nic.map(|l| (l, d)));
547
548 Self::add_ethtool_stats(&mut net_model, sample, last);
549 }
550
551 net_model
552 }
553}
554
555impl Nameable for SingleNetModel {
556 fn name() -> &'static str {
557 "network"
558 }
559}
560
561#[::below_derive::queriable_derives]
562pub struct SingleQueueModel {
563 pub interface: String,
564 pub queue_id: u32,
565 pub rx_bytes_per_sec: Option<u64>,
566 pub tx_bytes_per_sec: Option<u64>,
567 pub rx_count_per_sec: Option<u64>,
568 pub tx_count_per_sec: Option<u64>,
569 pub tx_missed_tx: Option<u64>,
570 pub tx_unmask_interrupt: Option<u64>,
571 pub raw_stats: BTreeMap<String, u64>,
572}
573
574impl SingleQueueModel {
575 fn new(
576 interface: &str,
577 queue_id: u32,
578 sample: ðtool::QueueStats,
579 last: Option<(ðtool::QueueStats, Duration)>,
580 ) -> Self {
581 SingleQueueModel {
582 interface: interface.to_string(),
583 queue_id,
584 rx_bytes_per_sec: get_option_rate!(rx_bytes, sample, last),
585 tx_bytes_per_sec: get_option_rate!(tx_bytes, sample, last),
586 rx_count_per_sec: get_option_rate!(rx_count, sample, last),
587 tx_count_per_sec: get_option_rate!(tx_count, sample, last),
588 tx_missed_tx: sample.tx_missed_tx,
589 tx_unmask_interrupt: sample.tx_unmask_interrupt,
590 raw_stats: sample.raw_stats.clone(),
591 }
592 }
593}
594
595impl Nameable for SingleQueueModel {
596 fn name() -> &'static str {
597 "ethtool_queue"
598 }
599}
600
601#[cfg(test)]
602mod test {
603 use super::*;
604
605 #[test]
606 fn query_model() {
607 let model_json = r#"
608 {
609 "interfaces": {
610 "eth0": {
611 "interface": "eth0",
612 "rx_bytes_per_sec": 42,
613 "tx_timeout_per_sec": 10,
614 "raw_stats": {
615 "stat0": 0
616 },
617 "queues": [
618 {
619 "interface": "eth0",
620 "queue_id": 0,
621 "rx_bytes_per_sec": 42,
622 "tx_bytes_per_sec": 1337,
623 "rx_count_per_sec": 10,
624 "tx_count_per_sec": 20,
625 "tx_missed_tx": 100,
626 "tx_unmask_interrupt": 200,
627 "raw_stats": {
628 "stat1": 1,
629 "stat2": 2
630 }
631 },
632 {
633 "interface": "eth0",
634 "queue_id": 1,
635 "rx_bytes_per_sec": 1337,
636 "tx_bytes_per_sec": 42,
637 "rx_count_per_sec": 20,
638 "tx_count_per_sec": 10,
639 "tx_missed_tx": 200,
640 "tx_unmask_interrupt": 100,
641 "raw_stats": {
642 "stat3": 3,
643 "stat4": 4
644 }
645 }
646 ]
647 }
648 },
649 "tcp": {},
650 "ip": {},
651 "ip6": {},
652 "icmp": {},
653 "icmp6": {},
654 "udp": {},
655 "udp6": {}
656 }
657 "#;
658 let model: NetworkModel = serde_json::from_str(model_json).unwrap();
659 assert_eq!(
660 model
661 .query(&NetworkModelFieldId::from_str("interfaces.eth0.rx_bytes_per_sec").unwrap()),
662 Some(Field::F64(42.0))
663 );
664
665 assert_eq!(
666 model.query(
667 &NetworkModelFieldId::from_str("interfaces.eth0.tx_timeout_per_sec").unwrap()
668 ),
669 Some(Field::U64(10))
670 );
671
672 assert_eq!(
673 model.query(
674 &NetworkModelFieldId::from_str("interfaces.eth0.queues.0.queue_id").unwrap()
675 ),
676 Some(Field::U32(0))
677 );
678 assert_eq!(
679 model.query(
680 &NetworkModelFieldId::from_str("interfaces.eth0.queues.0.rx_bytes_per_sec")
681 .unwrap()
682 ),
683 Some(Field::U64(42))
684 );
685
686 assert_eq!(
687 model.query(
688 &NetworkModelFieldId::from_str("interfaces.eth0.queues.1.queue_id").unwrap()
689 ),
690 Some(Field::U32(1))
691 );
692 }
693
694 #[test]
695 fn test_parse_ethtool_stats() {
696 let l_net_stats = procfs::NetStat::default();
697 let s_net_stats = procfs::NetStat::default();
698
699 let l_ethtool_stats = ethtool::EthtoolStats {
700 nic: BTreeMap::from([(
701 "eth0".to_string(),
702 ethtool::NicStats {
703 tx_timeout: Some(10),
704 raw_stats: BTreeMap::from([("stat0".to_string(), 0)]),
705 queue: vec![
706 ethtool::QueueStats {
707 rx_bytes: Some(42),
708 tx_bytes: Some(1337),
709 rx_count: Some(10),
710 tx_count: Some(20),
711 tx_missed_tx: Some(100),
712 tx_unmask_interrupt: Some(200),
713 raw_stats: vec![("stat1".to_string(), 1), ("stat2".to_string(), 2)]
714 .into_iter()
715 .collect(),
716 },
717 ethtool::QueueStats {
718 rx_bytes: Some(1337),
719 tx_bytes: Some(42),
720 rx_count: Some(20),
721 tx_count: Some(10),
722 tx_missed_tx: Some(200),
723 tx_unmask_interrupt: Some(100),
724 raw_stats: vec![("stat3".to_string(), 3), ("stat4".to_string(), 4)]
725 .into_iter()
726 .collect(),
727 },
728 ],
729 },
730 )]),
731 };
732
733 let s_ethtool_stats = ethtool::EthtoolStats {
734 nic: BTreeMap::from([(
735 "eth0".to_string(),
736 ethtool::NicStats {
737 tx_timeout: Some(20),
738 raw_stats: BTreeMap::from([("stat0".to_string(), 10)]),
739 queue: vec![
740 ethtool::QueueStats {
741 rx_bytes: Some(52),
742 tx_bytes: Some(1347),
743 rx_count: Some(20),
744 tx_count: Some(30),
745 tx_missed_tx: Some(110),
746 tx_unmask_interrupt: Some(210),
747 raw_stats: vec![("stat1".to_string(), 11), ("stat2".to_string(), 12)]
748 .into_iter()
749 .collect(),
750 },
751 ethtool::QueueStats {
752 rx_bytes: Some(1347),
753 tx_bytes: Some(52),
754 rx_count: Some(30),
755 tx_count: Some(20),
756 tx_missed_tx: Some(210),
757 tx_unmask_interrupt: Some(110),
758 raw_stats: vec![("stat3".to_string(), 13), ("stat4".to_string(), 14)]
759 .into_iter()
760 .collect(),
761 },
762 ],
763 },
764 )]),
765 };
766
767 let prev_sample = NetworkStats {
768 net: &l_net_stats,
769 ethtool: &Some(l_ethtool_stats),
770 };
771 let sample = NetworkStats {
772 net: &s_net_stats,
773 ethtool: &Some(s_ethtool_stats),
774 };
775 let last = Some((&prev_sample, Duration::from_secs(1)));
776
777 let model = NetworkModel::new(&sample, last);
778
779 let iface_model = model.interfaces.get("eth0").unwrap();
780 assert_eq!(iface_model.tx_timeout_per_sec, Some(10));
781 let nic_raw_stat = iface_model.raw_stats.get("stat0").unwrap();
782 assert_eq!(*nic_raw_stat, 10);
783
784 let queue_model = iface_model.queues.first().unwrap();
785 assert_eq!(queue_model.rx_bytes_per_sec, Some(10));
786 let queue_raw_stat = queue_model.raw_stats.get("stat1").unwrap();
788 assert_eq!(*queue_raw_stat, 11);
789
790 let queue_model = iface_model.queues.get(1).unwrap();
791 assert_eq!(queue_model.rx_bytes_per_sec, Some(10));
792 let queue_raw_stat = queue_model.raw_stats.get("stat3").unwrap();
793 assert_eq!(*queue_raw_stat, 13);
794 }
795}