M1: core engine, config, plugin model and core plugins
- Cargo workspace (core, plugins, tui, cli) with drop-in compatible CLI - Python-compatible config file handling, limits, views, rates, MMM, history - process collection via sysinfo + procfs, filters, thresholds, events, actions - 20 core plugins (cpu, percpu, mem, memswap, load, fs, network, diskio, ...) - stdout, stdout-csv, stdout-json, --issue, --modules-list output modes - differential test harness against the Python implementation
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
//! Disk I/O plugin.
|
||||
|
||||
use crate::collectors::{SECTOR_SIZE, read_diskstats};
|
||||
use crate::fields;
|
||||
use glances_core::plugin::{Field, HistoryItem, Plugin, PluginModel, UpdateContext};
|
||||
use glances_core::{Args, Config, Decoration};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct DiskioPlugin {
|
||||
pub model: PluginModel,
|
||||
}
|
||||
|
||||
impl DiskioPlugin {
|
||||
pub fn new(config: &Config, args: &Args) -> Self {
|
||||
let fields = fields! {
|
||||
"disk_name" => Field::new("Disk name."),
|
||||
"read_count" => Field::new("Number of reads.").rate().unit("number"),
|
||||
"write_count" => Field::new("Number of writes.").rate().unit("number"),
|
||||
"read_bytes" => Field::new("Number of bytes read.").rate().unit("byte"),
|
||||
"write_bytes" => Field::new("Number of bytes written.").rate().unit("byte"),
|
||||
"read_time" => Field::new("Time spent reading.").rate().unit("millisecond"),
|
||||
"write_time" => Field::new("Time spent writing.").rate().unit("millisecond"),
|
||||
"read_latency" => Field::new("Mean time spent reading per operation.").unit("millisecond"),
|
||||
"write_latency" => Field::new("Mean time spent writing per operation.").unit("millisecond"),
|
||||
};
|
||||
let items_history = vec![
|
||||
HistoryItem::new("read_bytes_rate_per_sec", "Bytes read per second"),
|
||||
HistoryItem::new("write_bytes_rate_per_sec", "Bytes write per second"),
|
||||
];
|
||||
let mut model = PluginModel::new(
|
||||
"diskio",
|
||||
fields,
|
||||
items_history,
|
||||
Value::Array(Vec::new()),
|
||||
config,
|
||||
args.time.unwrap_or(2.0),
|
||||
args.disable_history,
|
||||
args.disable_config_exec,
|
||||
);
|
||||
model.set_item_key("disk_name");
|
||||
model.hide_zero = config.get_bool_value("diskio", "hide_zero", false);
|
||||
model.hide_threshold_bytes = config.get_int_value("diskio", "hide_threshold_bytes", 0) as f64;
|
||||
model.hide_zero_fields =
|
||||
vec!["read_bytes_rate_per_sec".to_string(), "write_bytes_rate_per_sec".to_string()];
|
||||
Self { model }
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for DiskioPlugin {
|
||||
fn name(&self) -> &str {
|
||||
"diskio"
|
||||
}
|
||||
|
||||
fn model(&self) -> &PluginModel {
|
||||
&self.model
|
||||
}
|
||||
|
||||
fn model_mut(&mut self) -> &mut PluginModel {
|
||||
&mut self.model
|
||||
}
|
||||
|
||||
fn get_key(&self) -> Option<&'static str> {
|
||||
Some("disk_name")
|
||||
}
|
||||
|
||||
fn collect(&mut self, ctx: &mut UpdateContext) -> anyhow::Result<Value> {
|
||||
let mut stats = Vec::new();
|
||||
for (disk_name, disk_stat) in read_diskstats() {
|
||||
if !ctx.args.diskio_show_ramfs && disk_name.starts_with("ram") {
|
||||
continue;
|
||||
}
|
||||
if !self.model.is_display(&disk_name, "") {
|
||||
continue;
|
||||
}
|
||||
let mut item = serde_json::Map::new();
|
||||
item.insert("read_count".to_string(), json!(disk_stat.read_count));
|
||||
item.insert("write_count".to_string(), json!(disk_stat.write_count));
|
||||
item.insert("read_bytes".to_string(), json!(disk_stat.sectors_read * SECTOR_SIZE));
|
||||
item.insert("write_bytes".to_string(), json!(disk_stat.sectors_written * SECTOR_SIZE));
|
||||
item.insert("read_time".to_string(), json!(disk_stat.read_time));
|
||||
item.insert("write_time".to_string(), json!(disk_stat.write_time));
|
||||
item.insert("key".to_string(), json!("disk_name"));
|
||||
item.insert("disk_name".to_string(), json!(disk_name));
|
||||
if let Some(alias) = self.model.has_alias(&disk_name) {
|
||||
item.insert("alias".to_string(), json!(alias));
|
||||
}
|
||||
stats.push(Value::Object(item));
|
||||
}
|
||||
Ok(Value::Array(stats))
|
||||
}
|
||||
|
||||
fn post_collect(&mut self, _ctx: &mut UpdateContext) -> anyhow::Result<()> {
|
||||
if let Value::Array(items) = &mut self.model.stats {
|
||||
for item in items.iter_mut() {
|
||||
let Some(item) = item.as_object_mut() else {
|
||||
continue;
|
||||
};
|
||||
let read_time = item.get("read_time_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let read_count = item.get("read_count_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let write_time = item.get("write_time_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let write_count = item.get("write_count_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let read_latency = if item.contains_key("read_time_rate_per_sec") && read_count > 0.0 {
|
||||
(read_time / read_count).floor() as i64
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let write_latency = if item.contains_key("write_time_rate_per_sec") && write_count > 0.0 {
|
||||
(write_time / write_count).floor() as i64
|
||||
} else {
|
||||
0
|
||||
};
|
||||
item.insert("read_latency".to_string(), json!(read_latency));
|
||||
item.insert("write_latency".to_string(), json!(write_latency));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_views(&mut self) {
|
||||
let key = self.get_key();
|
||||
self.model.update_views(key);
|
||||
let items: Vec<(String, f64, f64, f64, f64)> = match &self.model.stats {
|
||||
Value::Array(list) => list
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
let name = item.get("disk_name")?.as_str()?.to_string();
|
||||
let read_rate = item.get("read_bytes_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let write_rate = item.get("write_bytes_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let read_latency = item.get("read_latency").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let write_latency = item.get("write_latency").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
Some((name, read_rate, write_rate, read_latency, write_latency))
|
||||
})
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
for (disk_name, read_rate, write_rate, read_latency, write_latency) in items {
|
||||
let alert_rx = self.model.get_alert(read_rate, 0.0, 100.0, true, false, Some("rx"), Some(&disk_name), false);
|
||||
let alert_tx = self.model.get_alert(write_rate, 0.0, 100.0, true, false, Some("tx"), Some(&disk_name), false);
|
||||
for field in ["read_bytes", "read_bytes_rate_per_sec"] {
|
||||
self.model.set_view_decoration(Some(&disk_name), field, alert_rx);
|
||||
}
|
||||
for field in ["write_bytes", "write_bytes_rate_per_sec"] {
|
||||
self.model.set_view_decoration(Some(&disk_name), field, alert_tx);
|
||||
}
|
||||
let mut alert_latency_rx =
|
||||
self.model.get_alert(read_latency, 0.0, 100.0, true, false, Some("rx_latency"), Some(&disk_name), false);
|
||||
let mut alert_latency_tx =
|
||||
self.model.get_alert(write_latency, 0.0, 100.0, true, false, Some("tx_latency"), Some(&disk_name), false);
|
||||
if alert_latency_rx == Decoration::Default {
|
||||
alert_latency_rx = self.model.get_alert(read_latency, 0.0, 100.0, true, false, Some("rx_latency"), None, false);
|
||||
}
|
||||
if alert_latency_tx == Decoration::Default {
|
||||
alert_latency_tx = self.model.get_alert(write_latency, 0.0, 100.0, true, false, Some("tx_latency"), None, false);
|
||||
}
|
||||
self.model.set_view_decoration(Some(&disk_name), "read_latency", alert_latency_rx);
|
||||
self.model.set_view_decoration(Some(&disk_name), "write_latency", alert_latency_tx);
|
||||
}
|
||||
}
|
||||
|
||||
fn display_curse(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user