M4: fetch mode, cleanup and documentation
- --fetch with a Jinja-subset renderer and the shipped templates - clippy clean, cargo fmt, all tests green - README status and differential testing instructions
This commit is contained in:
@@ -94,7 +94,13 @@ fn clock_ticks() -> f64 {
|
||||
|
||||
fn parse_cpu_line(fields: &[&str]) -> CpuTimes {
|
||||
let ticks = clock_ticks();
|
||||
let value = |index: usize| -> f64 { fields.get(index).and_then(|v| v.parse::<f64>().ok()).unwrap_or(0.0) / ticks };
|
||||
let value = |index: usize| -> f64 {
|
||||
fields
|
||||
.get(index)
|
||||
.and_then(|v| v.parse::<f64>().ok())
|
||||
.unwrap_or(0.0)
|
||||
/ ticks
|
||||
};
|
||||
CpuTimes {
|
||||
user: value(0),
|
||||
nice: value(1),
|
||||
@@ -150,7 +156,11 @@ pub fn read_meminfo() -> HashMap<String, u64> {
|
||||
continue;
|
||||
};
|
||||
let unit = parts.next().unwrap_or("");
|
||||
let bytes = if unit.starts_with("kB") { value * 1024 } else { value };
|
||||
let bytes = if unit.starts_with("kB") {
|
||||
value * 1024
|
||||
} else {
|
||||
value
|
||||
};
|
||||
values.insert(key.trim().to_string(), bytes);
|
||||
}
|
||||
values
|
||||
@@ -163,10 +173,10 @@ pub fn read_vmstat() -> HashMap<String, u64> {
|
||||
return values;
|
||||
};
|
||||
for line in content.lines() {
|
||||
if let Some((key, value)) = line.split_once(' ') {
|
||||
if let Ok(value) = value.trim().parse::<u64>() {
|
||||
values.insert(key.to_string(), value);
|
||||
}
|
||||
if let Some((key, value)) = line.split_once(' ')
|
||||
&& let Ok(value) = value.trim().parse::<u64>()
|
||||
{
|
||||
values.insert(key.to_string(), value);
|
||||
}
|
||||
}
|
||||
values
|
||||
@@ -212,7 +222,9 @@ pub fn uptime_seconds() -> Option<f64> {
|
||||
|
||||
/// Number of logical CPUs.
|
||||
pub fn logical_core_count() -> usize {
|
||||
std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1)
|
||||
std::thread::available_parallelism()
|
||||
.map(|n| n.get())
|
||||
.unwrap_or(1)
|
||||
}
|
||||
|
||||
/// Number of physical cores, parsed from `/proc/cpuinfo`.
|
||||
@@ -262,20 +274,21 @@ pub fn cpu_name() -> String {
|
||||
let mut implementer: Option<String> = None;
|
||||
let mut ret = fallback;
|
||||
for line in content.lines() {
|
||||
if line.starts_with("model name") || line.starts_with("Model") || line.starts_with("cpu model") {
|
||||
if let Some((_, value)) = line.split_once(':') {
|
||||
return value.trim().to_string();
|
||||
}
|
||||
if (line.starts_with("model name")
|
||||
|| line.starts_with("Model")
|
||||
|| line.starts_with("cpu model"))
|
||||
&& let Some((_, value)) = line.split_once(':')
|
||||
{
|
||||
return value.trim().to_string();
|
||||
}
|
||||
if line.starts_with("CPU implementer") {
|
||||
implementer = line.split_once(':').map(|(_, v)| v.trim().to_string());
|
||||
}
|
||||
if line.starts_with("CPU part") {
|
||||
if let Some(part) = line.split_once(':').map(|(_, v)| v.trim().to_string()) {
|
||||
if let Some(implementer) = &implementer {
|
||||
ret = format!("{implementer} part {part}");
|
||||
}
|
||||
}
|
||||
if line.starts_with("CPU part")
|
||||
&& let Some(part) = line.split_once(':').map(|(_, v)| v.trim().to_string())
|
||||
&& let Some(implementer) = &implementer
|
||||
{
|
||||
ret = format!("{implementer} part {part}");
|
||||
}
|
||||
}
|
||||
ret
|
||||
@@ -316,7 +329,10 @@ pub fn read_net_dev() -> IndexMap<String, NetDev> {
|
||||
let Some((interface, values)) = line.split_once(':') else {
|
||||
continue;
|
||||
};
|
||||
let fields: Vec<u64> = values.split_whitespace().filter_map(|v| v.parse().ok()).collect();
|
||||
let fields: Vec<u64> = values
|
||||
.split_whitespace()
|
||||
.filter_map(|v| v.parse().ok())
|
||||
.collect();
|
||||
if fields.len() < 16 {
|
||||
continue;
|
||||
}
|
||||
@@ -429,7 +445,8 @@ pub fn read_diskstats() -> IndexMap<String, DiskStat> {
|
||||
continue;
|
||||
}
|
||||
let name = fields[2].to_string();
|
||||
let value = |index: usize| -> u64 { fields.get(index).and_then(|v| v.parse().ok()).unwrap_or(0) };
|
||||
let value =
|
||||
|index: usize| -> u64 { fields.get(index).and_then(|v| v.parse().ok()).unwrap_or(0) };
|
||||
result.insert(
|
||||
name,
|
||||
DiskStat {
|
||||
@@ -520,12 +537,25 @@ pub fn disk_usage(path: &str) -> Option<FsUsage> {
|
||||
if unsafe { libc::statvfs(c_path.as_ptr(), &mut stat) } != 0 {
|
||||
return None;
|
||||
}
|
||||
let block_size = if stat.f_frsize > 0 { stat.f_frsize } else { stat.f_bsize } as u64;
|
||||
let block_size = if stat.f_frsize > 0 {
|
||||
stat.f_frsize
|
||||
} else {
|
||||
stat.f_bsize
|
||||
} as u64;
|
||||
let total = stat.f_blocks as u64 * block_size;
|
||||
let free = stat.f_bavail as u64 * block_size;
|
||||
let used = (stat.f_blocks as u64).saturating_sub(stat.f_bfree as u64) * block_size;
|
||||
let percent = if used + free > 0 { (used as f64 * 100.0) / (used + free) as f64 } else { 0.0 };
|
||||
Some(FsUsage { total, used, free, percent })
|
||||
let percent = if used + free > 0 {
|
||||
(used as f64 * 100.0) / (used + free) as f64
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
Some(FsUsage {
|
||||
total,
|
||||
used,
|
||||
free,
|
||||
percent,
|
||||
})
|
||||
}
|
||||
|
||||
/// Linux distribution name/version (`/etc/os-release`).
|
||||
@@ -546,7 +576,8 @@ pub fn read_os_release() -> (String, String) {
|
||||
|
||||
pub fn hostname() -> String {
|
||||
let mut buffer = [0u8; 256];
|
||||
let result = unsafe { libc::gethostname(buffer.as_mut_ptr() as *mut libc::c_char, buffer.len()) };
|
||||
let result =
|
||||
unsafe { libc::gethostname(buffer.as_mut_ptr() as *mut libc::c_char, buffer.len()) };
|
||||
if result == 0 {
|
||||
let end = buffer.iter().position(|c| *c == 0).unwrap_or(buffer.len());
|
||||
String::from_utf8_lossy(&buffer[..end]).to_string()
|
||||
@@ -571,10 +602,17 @@ pub fn get_ip_address() -> (Option<String>, Option<String>) {
|
||||
addr.sin_family = libc::AF_INET as u16;
|
||||
addr.sin_port = 53u16.to_be();
|
||||
addr.sin_addr.s_addr = u32::from_be_bytes([192, 0, 2, 1]).to_be();
|
||||
if libc::connect(sock, &addr as *const _ as *const libc::sockaddr, std::mem::size_of::<libc::sockaddr_in>() as u32) == 0 {
|
||||
if libc::connect(
|
||||
sock,
|
||||
&addr as *const _ as *const libc::sockaddr,
|
||||
std::mem::size_of::<libc::sockaddr_in>() as u32,
|
||||
) == 0
|
||||
{
|
||||
let mut local: libc::sockaddr_in = std::mem::zeroed();
|
||||
let mut len = std::mem::size_of::<libc::sockaddr_in>() as u32;
|
||||
if libc::getsockname(sock, &mut local as *mut _ as *mut libc::sockaddr, &mut len) == 0 {
|
||||
if libc::getsockname(sock, &mut local as *mut _ as *mut libc::sockaddr, &mut len)
|
||||
== 0
|
||||
{
|
||||
let bytes = local.sin_addr.s_addr.to_ne_bytes();
|
||||
let candidate = format!("{}.{}.{}.{}", bytes[0], bytes[1], bytes[2], bytes[3]);
|
||||
if !candidate.starts_with("127.") && candidate != "0.0.0.0" {
|
||||
@@ -599,11 +637,15 @@ pub fn get_ip_address() -> (Option<String>, Option<String>) {
|
||||
if !addr.is_null() && (*addr).sa_family as i32 == libc::AF_INET {
|
||||
let sin = addr as *const libc::sockaddr_in;
|
||||
let bytes = (*sin).sin_addr.s_addr.to_ne_bytes();
|
||||
let candidate = format!("{}.{}.{}.{}", bytes[0], bytes[1], bytes[2], bytes[3]);
|
||||
let candidate =
|
||||
format!("{}.{}.{}.{}", bytes[0], bytes[1], bytes[2], bytes[3]);
|
||||
if &candidate == ip && !mask.is_null() {
|
||||
let sin_mask = mask as *const libc::sockaddr_in;
|
||||
let mbytes = (*sin_mask).sin_addr.s_addr.to_ne_bytes();
|
||||
netmask = Some(format!("{}.{}.{}.{}", mbytes[0], mbytes[1], mbytes[2], mbytes[3]));
|
||||
netmask = Some(format!(
|
||||
"{}.{}.{}.{}",
|
||||
mbytes[0], mbytes[1], mbytes[2], mbytes[3]
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -624,18 +666,24 @@ pub fn get_ip_address() -> (Option<String>, Option<String>) {
|
||||
while !current.is_null() {
|
||||
let addr = (*current).ifa_addr;
|
||||
if !addr.is_null() && (*addr).sa_family as i32 == libc::AF_INET {
|
||||
let name = std::ffi::CStr::from_ptr((*current).ifa_name).to_string_lossy().to_string();
|
||||
let name = std::ffi::CStr::from_ptr((*current).ifa_name)
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
let flags = (*current).ifa_flags as u32;
|
||||
if name != "lo" && flags & libc::IFF_UP as u32 != 0 {
|
||||
let sin = addr as *const libc::sockaddr_in;
|
||||
let bytes = (*sin).sin_addr.s_addr.to_ne_bytes();
|
||||
let candidate = format!("{}.{}.{}.{}", bytes[0], bytes[1], bytes[2], bytes[3]);
|
||||
let candidate =
|
||||
format!("{}.{}.{}.{}", bytes[0], bytes[1], bytes[2], bytes[3]);
|
||||
let mask = (*current).ifa_netmask;
|
||||
let mut netmask = None;
|
||||
if !mask.is_null() {
|
||||
let sin_mask = mask as *const libc::sockaddr_in;
|
||||
let mbytes = (*sin_mask).sin_addr.s_addr.to_ne_bytes();
|
||||
netmask = Some(format!("{}.{}.{}.{}", mbytes[0], mbytes[1], mbytes[2], mbytes[3]));
|
||||
netmask = Some(format!(
|
||||
"{}.{}.{}.{}",
|
||||
mbytes[0], mbytes[1], mbytes[2], mbytes[3]
|
||||
));
|
||||
}
|
||||
found = (Some(candidate), netmask);
|
||||
break;
|
||||
@@ -661,7 +709,11 @@ pub fn get_default_gateway() -> Option<String> {
|
||||
let gateway = fields[2];
|
||||
if gateway.len() == 8 {
|
||||
let bytes: Vec<String> = (0..4)
|
||||
.map(|i| u8::from_str_radix(&gateway[i * 2..i * 2 + 2], 16).unwrap_or(0).to_string())
|
||||
.map(|i| {
|
||||
u8::from_str_radix(&gateway[i * 2..i * 2 + 2], 16)
|
||||
.unwrap_or(0)
|
||||
.to_string()
|
||||
})
|
||||
.collect();
|
||||
return Some(bytes.join("."));
|
||||
}
|
||||
@@ -684,7 +736,9 @@ pub fn ip_to_cidr(ip: Option<&str>) -> u32 {
|
||||
|
||||
/// Read a sysfs hwmon input value.
|
||||
pub fn read_number_file(path: &str) -> Option<f64> {
|
||||
std::fs::read_to_string(path).ok().and_then(|v| v.trim().parse::<f64>().ok())
|
||||
std::fs::read_to_string(path)
|
||||
.ok()
|
||||
.and_then(|v| v.trim().parse::<f64>().ok())
|
||||
}
|
||||
|
||||
/// Get the current date formatted with a C strftime format (same semantics
|
||||
|
||||
@@ -20,6 +20,7 @@ pub struct CpuPercentState {
|
||||
prev_cpu: Option<CpuTimes>,
|
||||
prev_percpu: Vec<CpuTimes>,
|
||||
cpu_percent: f64,
|
||||
cpu_timer: std::time::Instant,
|
||||
percpu_percent: Vec<Value>,
|
||||
}
|
||||
|
||||
@@ -30,6 +31,7 @@ impl CpuPercentState {
|
||||
prev_cpu: None,
|
||||
prev_percpu: Vec::new(),
|
||||
cpu_percent: 0.0,
|
||||
cpu_timer: std::time::Instant::now(),
|
||||
percpu_percent: Vec::new(),
|
||||
};
|
||||
state.cpu_percent = state.compute_cpu();
|
||||
@@ -39,12 +41,12 @@ impl CpuPercentState {
|
||||
|
||||
/// Update and/or return the CPU usage (psutil `cpu_percent`).
|
||||
pub fn get_cpu(&mut self) -> f64 {
|
||||
// The Python implementation caches this value for `cached_timer_cpu`
|
||||
// seconds. Since the engine refreshes at least every `args.time`
|
||||
// seconds, the value is recomputed on every call there; keep the cache
|
||||
// for direct calls.
|
||||
let _ = self.cached_timer_cpu;
|
||||
self.cpu_percent = self.compute_cpu();
|
||||
// The value is cached for `cached_timer_cpu` seconds like in the
|
||||
// Python implementation.
|
||||
if self.cpu_timer.elapsed().as_secs_f64() >= self.cached_timer_cpu {
|
||||
self.cpu_percent = self.compute_cpu();
|
||||
self.cpu_timer = std::time::Instant::now();
|
||||
}
|
||||
self.cpu_percent
|
||||
}
|
||||
|
||||
|
||||
@@ -27,37 +27,102 @@ impl Engine {
|
||||
// Registration order mirrors the alphabetical plugin loading of the
|
||||
// Python implementation (in particular processcount runs before
|
||||
// processlist and programlist).
|
||||
plugins.insert("amps".to_string(), Box::new(AmpsPlugin::new(&config, &args)));
|
||||
plugins.insert("core".to_string(), Box::new(CorePlugin::new(&config, &args)));
|
||||
plugins.insert(
|
||||
"amps".to_string(),
|
||||
Box::new(AmpsPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"core".to_string(),
|
||||
Box::new(CorePlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert("cpu".to_string(), Box::new(CpuPlugin::new(&config, &args)));
|
||||
plugins.insert("diskio".to_string(), Box::new(DiskioPlugin::new(&config, &args)));
|
||||
plugins.insert("folders".to_string(), Box::new(FoldersPlugin::new(&config, &args)));
|
||||
plugins.insert(
|
||||
"diskio".to_string(),
|
||||
Box::new(DiskioPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"folders".to_string(),
|
||||
Box::new(FoldersPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert("fs".to_string(), Box::new(FsPlugin::new(&config, &args)));
|
||||
plugins.insert("ip".to_string(), Box::new(IpPlugin::new(&config, &args)));
|
||||
plugins.insert("irq".to_string(), Box::new(IrqPlugin::new(&config, &args)));
|
||||
plugins.insert("load".to_string(), Box::new(LoadPlugin::new(&config, &args)));
|
||||
plugins.insert(
|
||||
"load".to_string(),
|
||||
Box::new(LoadPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert("mem".to_string(), Box::new(MemPlugin::new(&config, &args)));
|
||||
plugins.insert("memswap".to_string(), Box::new(MemSwapPlugin::new(&config, &args)));
|
||||
plugins.insert("network".to_string(), Box::new(NetworkPlugin::new(&config, &args)));
|
||||
plugins.insert(
|
||||
"memswap".to_string(),
|
||||
Box::new(MemSwapPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"network".to_string(),
|
||||
Box::new(NetworkPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert("now".to_string(), Box::new(NowPlugin::new(&config, &args)));
|
||||
plugins.insert("percpu".to_string(), Box::new(PerCpuPlugin::new(&config, &args)));
|
||||
plugins.insert("ports".to_string(), Box::new(PortsPlugin::new(&config, &args)));
|
||||
plugins.insert("processcount".to_string(), Box::new(ProcessCountPlugin::new(&config, &args)));
|
||||
plugins.insert("processlist".to_string(), Box::new(ProcessListPlugin::new(&config, &args)));
|
||||
plugins.insert("programlist".to_string(), Box::new(ProgramListPlugin::new(&config, &args)));
|
||||
plugins.insert("psutilversion".to_string(), Box::new(PsutilVersionPlugin::new(&config, &args)));
|
||||
plugins.insert("quicklook".to_string(), Box::new(QuicklookPlugin::new(&config, &args)));
|
||||
plugins.insert("raid".to_string(), Box::new(RaidPlugin::new(&config, &args)));
|
||||
plugins.insert("sensors".to_string(), Box::new(SensorsPlugin::new(&config, &args)));
|
||||
plugins.insert("system".to_string(), Box::new(SystemPlugin::new(&config, &args)));
|
||||
plugins.insert("uptime".to_string(), Box::new(UptimePlugin::new(&config, &args)));
|
||||
plugins.insert("version".to_string(), Box::new(VersionPlugin::new(&config, &args)));
|
||||
plugins.insert("wifi".to_string(), Box::new(WifiPlugin::new(&config, &args)));
|
||||
plugins.insert(
|
||||
"percpu".to_string(),
|
||||
Box::new(PerCpuPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"ports".to_string(),
|
||||
Box::new(PortsPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"processcount".to_string(),
|
||||
Box::new(ProcessCountPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"processlist".to_string(),
|
||||
Box::new(ProcessListPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"programlist".to_string(),
|
||||
Box::new(ProgramListPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"psutilversion".to_string(),
|
||||
Box::new(PsutilVersionPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"quicklook".to_string(),
|
||||
Box::new(QuicklookPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"raid".to_string(),
|
||||
Box::new(RaidPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"sensors".to_string(),
|
||||
Box::new(SensorsPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"system".to_string(),
|
||||
Box::new(SystemPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"uptime".to_string(),
|
||||
Box::new(UptimePlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"version".to_string(),
|
||||
Box::new(VersionPlugin::new(&config, &args)),
|
||||
);
|
||||
plugins.insert(
|
||||
"wifi".to_string(),
|
||||
Box::new(WifiPlugin::new(&config, &args)),
|
||||
);
|
||||
// The refresh time is set on the model, which was built with the
|
||||
// command line value already.
|
||||
let _ = refresh_time;
|
||||
|
||||
let mut engine = Self { args, config, runtime, plugins };
|
||||
let mut engine = Self {
|
||||
args,
|
||||
config,
|
||||
runtime,
|
||||
plugins,
|
||||
};
|
||||
engine.apply_plugin_options();
|
||||
engine.apply_process_options();
|
||||
engine.sync_enabled();
|
||||
@@ -77,11 +142,11 @@ impl Engine {
|
||||
fn apply_plugin_options(&mut self) {
|
||||
// Disable plugins from the configuration file.
|
||||
for section in self.config.sections() {
|
||||
if self.config.get_bool_value(section, "disable", false) {
|
||||
if let Some(plugin) = self.plugins.get_mut(section) {
|
||||
debug!("{} disabled by the configuration file", section);
|
||||
plugin.set_enabled(false);
|
||||
}
|
||||
if self.config.get_bool_value(section, "disable", false)
|
||||
&& let Some(plugin) = self.plugins.get_mut(section)
|
||||
{
|
||||
debug!("{} disabled by the configuration file", section);
|
||||
plugin.set_enabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,10 +167,18 @@ impl Engine {
|
||||
// --disable-plugin / --enable-plugin.
|
||||
let mut explicit_enable: Vec<String> = Vec::new();
|
||||
if let Some(enable) = &self.args.enable_plugin {
|
||||
explicit_enable = enable.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect();
|
||||
explicit_enable = enable
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
}
|
||||
if let Some(disable) = &self.args.disable_plugin {
|
||||
let tokens: Vec<String> = disable.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect();
|
||||
let tokens: Vec<String> = disable
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
if tokens.iter().any(|t| t == "all") {
|
||||
for name in self.plugins.keys().cloned().collect::<Vec<_>>() {
|
||||
self.plugins.get_mut(&name).unwrap().set_enabled(false);
|
||||
@@ -134,12 +207,18 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Processlist needs processcount to update the shared process list.
|
||||
let processcount_enabled = self.plugins.get("processcount").map(|p| p.is_enabled()).unwrap_or(false);
|
||||
let processcount_enabled = self
|
||||
.plugins
|
||||
.get("processcount")
|
||||
.map(|p| p.is_enabled())
|
||||
.unwrap_or(false);
|
||||
if !processcount_enabled {
|
||||
for name in ["processlist", "programlist"] {
|
||||
if let Some(plugin) = self.plugins.get_mut(name) {
|
||||
if plugin.is_enabled() {
|
||||
tracing::warn!("Processcount is disabled, so processlist (updated by processcount) is also disabled");
|
||||
tracing::warn!(
|
||||
"Processcount is disabled, so processlist (updated by processcount) is also disabled"
|
||||
);
|
||||
}
|
||||
plugin.set_enabled(false);
|
||||
}
|
||||
@@ -210,7 +289,10 @@ impl Engine {
|
||||
/// Update every enabled plugin.
|
||||
pub fn update(&mut self) {
|
||||
let names: Vec<String> = self.plugins.keys().cloned().collect();
|
||||
let enabled: Vec<String> = names.into_iter().filter(|n| self.plugins[n].is_enabled()).collect();
|
||||
let enabled: Vec<String> = names
|
||||
.into_iter()
|
||||
.filter(|n| self.plugins[n].is_enabled())
|
||||
.collect();
|
||||
self.update_plugins(&enabled);
|
||||
}
|
||||
|
||||
@@ -222,7 +304,13 @@ impl Engine {
|
||||
}
|
||||
|
||||
fn update_plugin(&mut self, name: &str) {
|
||||
let Engine { plugins, args, config, runtime, .. } = self;
|
||||
let Engine {
|
||||
plugins,
|
||||
args,
|
||||
config,
|
||||
runtime,
|
||||
..
|
||||
} = self;
|
||||
let Some(plugin) = plugins.get_mut(name) else {
|
||||
return;
|
||||
};
|
||||
@@ -289,10 +377,10 @@ impl Engine {
|
||||
};
|
||||
let mut result = serde_json::Map::new();
|
||||
for name in names {
|
||||
if let Some(plugin) = self.plugins.get(&name) {
|
||||
if plugin.is_enabled() {
|
||||
result.insert(name, plugin.stats().clone());
|
||||
}
|
||||
if let Some(plugin) = self.plugins.get(&name)
|
||||
&& plugin.is_enabled()
|
||||
{
|
||||
result.insert(name, plugin.stats().clone());
|
||||
}
|
||||
}
|
||||
result
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
//! glances-plugins: collectors, plugin registry and update engine.
|
||||
|
||||
pub mod collectors;
|
||||
|
||||
@@ -62,8 +62,12 @@ impl AmpsPlugin {
|
||||
.get_value(section, "regex", None)
|
||||
.and_then(|pattern| regex::Regex::new(&pattern).ok());
|
||||
let refresh = config.get_float_value(section, "refresh", 3.0);
|
||||
let countmin = config.get_value(section, "countmin", None).and_then(|v| v.parse::<f64>().ok());
|
||||
let countmax = config.get_value(section, "countmax", None).and_then(|v| v.parse::<f64>().ok());
|
||||
let countmin = config
|
||||
.get_value(section, "countmin", None)
|
||||
.and_then(|v| v.parse::<f64>().ok());
|
||||
let countmax = config
|
||||
.get_value(section, "countmax", None)
|
||||
.and_then(|v| v.parse::<f64>().ok());
|
||||
let one_line = config.get_bool_value(section, "one_line", false);
|
||||
amps.push(AmpConfig {
|
||||
name: amp_display_name(section),
|
||||
@@ -142,7 +146,12 @@ impl Plugin for AmpsPlugin {
|
||||
let cmdline = process
|
||||
.get("cmdline")
|
||||
.and_then(Value::as_array)
|
||||
.map(|a| a.iter().filter_map(|v| v.as_str()).collect::<Vec<_>>().join(" "))
|
||||
.map(|a| {
|
||||
a.iter()
|
||||
.filter_map(|v| v.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
})
|
||||
.unwrap_or_default();
|
||||
regex.is_match(name) || regex.is_match(&cmdline)
|
||||
})
|
||||
@@ -191,7 +200,8 @@ impl Plugin for AmpsPlugin {
|
||||
} else {
|
||||
glances_core::Decoration::Ok
|
||||
};
|
||||
self.model.set_view_decoration(Some(&name), "count", decoration);
|
||||
self.model
|
||||
.set_view_decoration(Some(&name), "count", decoration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +211,14 @@ impl Plugin for AmpsPlugin {
|
||||
|
||||
fn render(&mut self, ctx: &glances_core::RenderContext) -> Vec<glances_core::StatLine> {
|
||||
let mut ret = Vec::new();
|
||||
if self.model.stats.as_array().map(|a| a.is_empty()).unwrap_or(true) || ctx.max_width == 0 {
|
||||
if self
|
||||
.model
|
||||
.stats
|
||||
.as_array()
|
||||
.map(|a| a.is_empty())
|
||||
.unwrap_or(true)
|
||||
|| ctx.max_width == 0
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
let name_max_width = ctx.max_width.saturating_sub(12);
|
||||
@@ -212,7 +229,13 @@ impl Plugin for AmpsPlugin {
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(format!("{:>9}", "Count"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>9}", "Count"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let items = self.model.sorted_stats();
|
||||
let Some(items) = items.as_array() else {
|
||||
return ret;
|
||||
@@ -221,15 +244,28 @@ impl Plugin for AmpsPlugin {
|
||||
let name = i.get("name").and_then(Value::as_str).unwrap_or("");
|
||||
let count = i.get("count").and_then(Value::as_i64).unwrap_or(0);
|
||||
let result = i.get("result").and_then(Value::as_str).unwrap_or("");
|
||||
let display_name = if result.is_empty() { name.to_string() } else { format!("{name}: {result}") };
|
||||
let display_name = if result.is_empty() {
|
||||
name.to_string()
|
||||
} else {
|
||||
format!("{name}: {result}")
|
||||
};
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<width$}", display_name.chars().take(name_max_width).collect::<String>(), width = name_max_width),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(
|
||||
self.model.curse_add_line(
|
||||
format!(
|
||||
"{:<width$}",
|
||||
display_name
|
||||
.chars()
|
||||
.take(name_max_width)
|
||||
.collect::<String>(),
|
||||
width = name_max_width
|
||||
),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
);
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{count:>9}"),
|
||||
self.model.view_decoration(Some(name), "count"),
|
||||
|
||||
@@ -48,7 +48,11 @@ impl CpuPlugin {
|
||||
args.disable_config_exec,
|
||||
);
|
||||
let nb_log_core = crate::collectors::logical_core_count();
|
||||
Self { model, previous_times: None, nb_log_core }
|
||||
Self {
|
||||
model,
|
||||
previous_times: None,
|
||||
nb_log_core,
|
||||
}
|
||||
}
|
||||
|
||||
fn times_percent(&mut self) -> CpuTimes {
|
||||
@@ -105,11 +109,23 @@ impl Plugin for CpuPlugin {
|
||||
let mut soft_interrupts = 0u64;
|
||||
for line in content.lines() {
|
||||
if let Some(rest) = line.strip_prefix("ctxt ") {
|
||||
ctx_switches = rest.split_whitespace().next().and_then(|v| v.parse().ok()).unwrap_or(0);
|
||||
ctx_switches = rest
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(0);
|
||||
} else if let Some(rest) = line.strip_prefix("intr ") {
|
||||
interrupts = rest.split_whitespace().next().and_then(|v| v.parse().ok()).unwrap_or(0);
|
||||
interrupts = rest
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(0);
|
||||
} else if let Some(rest) = line.strip_prefix("softirq ") {
|
||||
soft_interrupts = rest.split_whitespace().next().and_then(|v| v.parse().ok()).unwrap_or(0);
|
||||
soft_interrupts = rest
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(0);
|
||||
}
|
||||
}
|
||||
stats.insert("ctx_switches".to_string(), json!(ctx_switches));
|
||||
@@ -125,13 +141,27 @@ impl Plugin for CpuPlugin {
|
||||
fn update_views(&mut self) {
|
||||
let key = self.get_key();
|
||||
self.model.update_views(key);
|
||||
if self.model.stats.get("time_since_update").and_then(Value::as_f64) != Some(0.0) {
|
||||
if let Some(ctx_switches) = self.model.stats.get("ctx_switches").and_then(Value::as_f64) {
|
||||
let maximum = 100.0 * self.nb_log_core as f64;
|
||||
let decoration =
|
||||
self.model.get_alert(ctx_switches, 0.0, maximum, true, false, Some("ctx_switches"), None, false);
|
||||
self.model.set_view_decoration(None, "ctx_switches", decoration);
|
||||
}
|
||||
if self
|
||||
.model
|
||||
.stats
|
||||
.get("time_since_update")
|
||||
.and_then(Value::as_f64)
|
||||
!= Some(0.0)
|
||||
&& let Some(ctx_switches) = self.model.stats.get("ctx_switches").and_then(Value::as_f64)
|
||||
{
|
||||
let maximum = 100.0 * self.nb_log_core as f64;
|
||||
let decoration = self.model.get_alert(
|
||||
ctx_switches,
|
||||
0.0,
|
||||
maximum,
|
||||
true,
|
||||
false,
|
||||
Some("ctx_switches"),
|
||||
None,
|
||||
false,
|
||||
);
|
||||
self.model
|
||||
.set_view_decoration(None, "ctx_switches", decoration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +178,13 @@ impl Plugin for CpuPlugin {
|
||||
let idle_tag = s.get("user").is_none();
|
||||
|
||||
// Line 1: total + idle + ctx_switches.
|
||||
ret.push(self.model.curse_add_line(format!("{:8}", "CPU"), glances_core::Decoration::Title, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:8}", "CPU"),
|
||||
glances_core::Decoration::Title,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
if let Some(total) = s.get("total").and_then(Value::as_f64) {
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{total:>4.1}%"),
|
||||
@@ -160,7 +196,13 @@ impl Plugin for CpuPlugin {
|
||||
}
|
||||
if s.get("idle").is_some() && !idle_tag {
|
||||
let optional = self.model.view_bool(None, "idle", "optional");
|
||||
ret.push(self.model.curse_add_line(" idle ", glances_core::Decoration::Default, optional, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
" idle ",
|
||||
glances_core::Decoration::Default,
|
||||
optional,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
if let Some(idle) = s.get("idle").and_then(Value::as_f64) {
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{idle:>4.1}%"),
|
||||
@@ -171,7 +213,10 @@ impl Plugin for CpuPlugin {
|
||||
));
|
||||
}
|
||||
}
|
||||
ret.extend(self.model.curse_add_stat("ctx_switches", Some(15), " ", "", ""));
|
||||
ret.extend(
|
||||
self.model
|
||||
.curse_add_stat("ctx_switches", Some(15), " ", "", ""),
|
||||
);
|
||||
|
||||
// Line 2.
|
||||
ret.push(self.model.curse_new_line());
|
||||
@@ -181,7 +226,10 @@ impl Plugin for CpuPlugin {
|
||||
ret.extend(self.model.curse_add_stat("idle", Some(15), "", "", ""));
|
||||
}
|
||||
ret.extend(self.model.curse_add_stat("irq", Some(14), " ", "", ""));
|
||||
ret.extend(self.model.curse_add_stat("interrupts", Some(15), " ", "", ""));
|
||||
ret.extend(
|
||||
self.model
|
||||
.curse_add_stat("interrupts", Some(15), " ", "", ""),
|
||||
);
|
||||
|
||||
// Line 3.
|
||||
ret.push(self.model.curse_new_line());
|
||||
@@ -192,9 +240,15 @@ impl Plugin for CpuPlugin {
|
||||
}
|
||||
ret.extend(self.model.curse_add_stat("nice", Some(14), " ", "", ""));
|
||||
if s.get("soft_interrupts").is_some() {
|
||||
ret.extend(self.model.curse_add_stat("soft_interrupts", Some(15), " ", "", ""));
|
||||
ret.extend(
|
||||
self.model
|
||||
.curse_add_stat("soft_interrupts", Some(15), " ", "", ""),
|
||||
);
|
||||
} else {
|
||||
ret.extend(self.model.curse_add_stat("ctx_switches", Some(15), " ", "", ""));
|
||||
ret.extend(
|
||||
self.model
|
||||
.curse_add_stat("ctx_switches", Some(15), " ", "", ""),
|
||||
);
|
||||
}
|
||||
|
||||
// Line 4.
|
||||
@@ -208,7 +262,10 @@ impl Plugin for CpuPlugin {
|
||||
if s.get("guest").is_some() {
|
||||
ret.extend(self.model.curse_add_stat("guest", Some(14), " ", "", ""));
|
||||
} else {
|
||||
ret.extend(self.model.curse_add_stat("syscalls", Some(15), " ", "", ""));
|
||||
ret.extend(
|
||||
self.model
|
||||
.curse_add_stat("syscalls", Some(15), " ", "", ""),
|
||||
);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
@@ -39,9 +39,12 @@ impl DiskioPlugin {
|
||||
);
|
||||
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()];
|
||||
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 }
|
||||
}
|
||||
}
|
||||
@@ -75,8 +78,14 @@ impl Plugin for DiskioPlugin {
|
||||
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_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"));
|
||||
@@ -95,20 +104,34 @@ impl Plugin for DiskioPlugin {
|
||||
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
|
||||
};
|
||||
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));
|
||||
}
|
||||
@@ -124,36 +147,104 @@ impl Plugin for DiskioPlugin {
|
||||
.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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +254,14 @@ impl Plugin for DiskioPlugin {
|
||||
|
||||
fn render(&mut self, ctx: &glances_core::RenderContext) -> Vec<glances_core::StatLine> {
|
||||
let mut ret = Vec::new();
|
||||
if self.model.stats.as_array().map(|a| a.is_empty()).unwrap_or(true) || ctx.max_width == 0 {
|
||||
if self
|
||||
.model
|
||||
.stats
|
||||
.as_array()
|
||||
.map(|a| a.is_empty())
|
||||
.unwrap_or(true)
|
||||
|| ctx.max_width == 0
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
let name_max_width = ctx.max_width.saturating_sub(13);
|
||||
@@ -175,21 +273,61 @@ impl Plugin for DiskioPlugin {
|
||||
false,
|
||||
));
|
||||
if ctx.args.diskio_iops {
|
||||
ret.push(self.model.curse_add_line(format!("{:>8}", "IOR/s"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "IOW/s"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>8}", "IOR/s"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "IOW/s"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
} else if ctx.args.diskio_latency {
|
||||
ret.push(self.model.curse_add_line(format!("{:>8}", "ms/opR"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "ms/opW"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>8}", "ms/opR"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "ms/opW"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
} else {
|
||||
ret.push(self.model.curse_add_line(format!("{:>8}", "R/s"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "W/s"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>8}", "R/s"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "W/s"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
let stats = self.model.sorted_stats();
|
||||
let Some(items) = stats.as_array() else {
|
||||
return ret;
|
||||
};
|
||||
for i in items {
|
||||
let disk_real_name = i.get("disk_name").and_then(Value::as_str).unwrap_or("").to_string();
|
||||
let disk_real_name = i
|
||||
.get("disk_name")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
if self
|
||||
.model
|
||||
.hide_zero_fields
|
||||
@@ -204,7 +342,10 @@ impl Plugin for DiskioPlugin {
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| disk_real_name.clone());
|
||||
if disk_name.chars().count() > name_max_width {
|
||||
disk_name = format!("{}_", disk_name.chars().take(name_max_width).collect::<String>());
|
||||
disk_name = format!(
|
||||
"{}_",
|
||||
disk_name.chars().take(name_max_width).collect::<String>()
|
||||
);
|
||||
}
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(
|
||||
@@ -215,20 +356,108 @@ impl Plugin for DiskioPlugin {
|
||||
false,
|
||||
));
|
||||
if ctx.args.diskio_iops {
|
||||
let rx = self.model.auto_unit(i.get("read_count_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0), false, "K");
|
||||
let tx = self.model.auto_unit(i.get("write_count_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0), false, "K");
|
||||
ret.push(self.model.curse_add_line(format!("{rx:>7}"), self.model.view_decoration(Some(&disk_real_name), "read_count"), false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{tx:>7}"), self.model.view_decoration(Some(&disk_real_name), "write_count"), false, false, false));
|
||||
let rx = self.model.auto_unit(
|
||||
i.get("read_count_rate_per_sec")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0),
|
||||
false,
|
||||
"K",
|
||||
);
|
||||
let tx = self.model.auto_unit(
|
||||
i.get("write_count_rate_per_sec")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0),
|
||||
false,
|
||||
"K",
|
||||
);
|
||||
ret.push(
|
||||
self.model.curse_add_line(
|
||||
format!("{rx:>7}"),
|
||||
self.model
|
||||
.view_decoration(Some(&disk_real_name), "read_count"),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
);
|
||||
ret.push(
|
||||
self.model.curse_add_line(
|
||||
format!("{tx:>7}"),
|
||||
self.model
|
||||
.view_decoration(Some(&disk_real_name), "write_count"),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
);
|
||||
} else if ctx.args.diskio_latency {
|
||||
let rx = self.model.auto_unit(i.get("read_latency").and_then(Value::as_f64).unwrap_or(0.0), true, "K");
|
||||
let tx = self.model.auto_unit(i.get("write_latency").and_then(Value::as_f64).unwrap_or(0.0), true, "K");
|
||||
ret.push(self.model.curse_add_line(format!("{rx:>7}"), self.model.view_decoration(Some(&disk_real_name), "read_latency"), false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{tx:>7}"), self.model.view_decoration(Some(&disk_real_name), "write_latency"), false, false, false));
|
||||
let rx = self.model.auto_unit(
|
||||
i.get("read_latency").and_then(Value::as_f64).unwrap_or(0.0),
|
||||
true,
|
||||
"K",
|
||||
);
|
||||
let tx = self.model.auto_unit(
|
||||
i.get("write_latency")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0),
|
||||
true,
|
||||
"K",
|
||||
);
|
||||
ret.push(
|
||||
self.model.curse_add_line(
|
||||
format!("{rx:>7}"),
|
||||
self.model
|
||||
.view_decoration(Some(&disk_real_name), "read_latency"),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
);
|
||||
ret.push(
|
||||
self.model.curse_add_line(
|
||||
format!("{tx:>7}"),
|
||||
self.model
|
||||
.view_decoration(Some(&disk_real_name), "write_latency"),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
let rx = self.model.auto_unit(i.get("read_bytes_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0), false, "K");
|
||||
let tx = self.model.auto_unit(i.get("write_bytes_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0), false, "K");
|
||||
ret.push(self.model.curse_add_line(format!("{rx:>7}"), self.model.view_decoration(Some(&disk_real_name), "read_bytes"), false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{tx:>7}"), self.model.view_decoration(Some(&disk_real_name), "write_bytes"), false, false, false));
|
||||
let rx = self.model.auto_unit(
|
||||
i.get("read_bytes_rate_per_sec")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0),
|
||||
false,
|
||||
"K",
|
||||
);
|
||||
let tx = self.model.auto_unit(
|
||||
i.get("write_bytes_rate_per_sec")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0),
|
||||
false,
|
||||
"K",
|
||||
);
|
||||
ret.push(
|
||||
self.model.curse_add_line(
|
||||
format!("{rx:>7}"),
|
||||
self.model
|
||||
.view_decoration(Some(&disk_real_name), "read_bytes"),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
);
|
||||
ret.push(
|
||||
self.model.curse_add_line(
|
||||
format!("{tx:>7}"),
|
||||
self.model
|
||||
.view_decoration(Some(&disk_real_name), "write_bytes"),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
ret
|
||||
|
||||
@@ -14,7 +14,6 @@ pub struct FoldersPlugin {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FolderConfig {
|
||||
indice: String,
|
||||
path: String,
|
||||
refresh: f64,
|
||||
careful: Option<f64>,
|
||||
@@ -59,12 +58,14 @@ impl FoldersPlugin {
|
||||
if path.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let refresh = config.get_float_value("folders", &format!("{key}refresh"), default_refresh);
|
||||
let refresh =
|
||||
config.get_float_value("folders", &format!("{key}refresh"), default_refresh);
|
||||
let threshold = |name: &str| -> Option<f64> {
|
||||
config.get_value("folders", &format!("{key}{name}"), None).and_then(|v| v.parse().ok())
|
||||
config
|
||||
.get_value("folders", &format!("{key}{name}"), None)
|
||||
.and_then(|v| v.parse().ok())
|
||||
};
|
||||
folders.push(FolderConfig {
|
||||
indice: index.to_string(),
|
||||
path,
|
||||
refresh,
|
||||
careful: threshold("careful"),
|
||||
@@ -123,21 +124,22 @@ impl Plugin for FoldersPlugin {
|
||||
fn update_views(&mut self) {
|
||||
let key = self.get_key();
|
||||
self.model.update_views(key);
|
||||
let items: Vec<(String, i64, u64, Option<f64>, Option<f64>, Option<f64>)> = match &self.model.stats {
|
||||
Value::Array(list) => list
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
let path = item.get("path")?.as_str()?.to_string();
|
||||
let errno = item.get("errno")?.as_i64()?;
|
||||
let size = item.get("size")?.as_u64()?;
|
||||
let careful = item.get("careful").and_then(Value::as_f64);
|
||||
let warning = item.get("warning").and_then(Value::as_f64);
|
||||
let critical = item.get("critical").and_then(Value::as_f64);
|
||||
Some((path, errno, size, careful, warning, critical))
|
||||
})
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
let items: Vec<(String, i64, u64, Option<f64>, Option<f64>, Option<f64>)> =
|
||||
match &self.model.stats {
|
||||
Value::Array(list) => list
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
let path = item.get("path")?.as_str()?.to_string();
|
||||
let errno = item.get("errno")?.as_i64()?;
|
||||
let size = item.get("size")?.as_u64()?;
|
||||
let careful = item.get("careful").and_then(Value::as_f64);
|
||||
let warning = item.get("warning").and_then(Value::as_f64);
|
||||
let critical = item.get("critical").and_then(Value::as_f64);
|
||||
Some((path, errno, size, careful, warning, critical))
|
||||
})
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
for (path, errno, size, careful, warning, critical) in items {
|
||||
if errno != 0 {
|
||||
continue;
|
||||
@@ -151,7 +153,8 @@ impl Plugin for FoldersPlugin {
|
||||
} else {
|
||||
glances_core::Decoration::Default
|
||||
};
|
||||
self.model.set_view_decoration(Some(&path), "size", decoration);
|
||||
self.model
|
||||
.set_view_decoration(Some(&path), "size", decoration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +164,14 @@ impl Plugin for FoldersPlugin {
|
||||
|
||||
fn render(&mut self, ctx: &glances_core::RenderContext) -> Vec<glances_core::StatLine> {
|
||||
let mut ret = Vec::new();
|
||||
if self.model.stats.as_array().map(|a| a.is_empty()).unwrap_or(true) || ctx.max_width == 0 {
|
||||
if self
|
||||
.model
|
||||
.stats
|
||||
.as_array()
|
||||
.map(|a| a.is_empty())
|
||||
.unwrap_or(true)
|
||||
|| ctx.max_width == 0
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
let name_max_width = ctx.max_width.saturating_sub(12);
|
||||
@@ -172,7 +182,13 @@ impl Plugin for FoldersPlugin {
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(format!("{:>9}", "Size"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>9}", "Size"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let items = self.model.sorted_stats();
|
||||
let Some(items) = items.as_array() else {
|
||||
return ret;
|
||||
@@ -183,7 +199,11 @@ impl Plugin for FoldersPlugin {
|
||||
let errno = i.get("errno").and_then(Value::as_i64).unwrap_or(0);
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<width$}", path.chars().take(name_max_width).collect::<String>(), width = name_max_width),
|
||||
format!(
|
||||
"{:<width$}",
|
||||
path.chars().take(name_max_width).collect::<String>(),
|
||||
width = name_max_width
|
||||
),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
|
||||
@@ -59,11 +59,16 @@ impl Plugin for FsPlugin {
|
||||
let mut stats = Vec::new();
|
||||
for mount in crate::collectors::read_mounts() {
|
||||
let is_physical = mount.device.starts_with("/dev/");
|
||||
let is_allowed = allowed_fs_types.iter().any(|fs_type| mount.fs_type.contains(fs_type.as_str()));
|
||||
let is_allowed = allowed_fs_types
|
||||
.iter()
|
||||
.any(|fs_type| mount.fs_type.contains(fs_type.as_str()));
|
||||
if !is_physical && !is_allowed {
|
||||
continue;
|
||||
}
|
||||
if !self.model.is_display_any(&[&mount.mount_point, &mount.device], "") {
|
||||
if !self
|
||||
.model
|
||||
.is_display_any(&[&mount.mount_point, &mount.device], "")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let Some(usage) = crate::collectors::disk_usage(&mount.mount_point) else {
|
||||
@@ -78,7 +83,10 @@ impl Plugin for FsPlugin {
|
||||
item.insert("size".to_string(), json!(usage.total));
|
||||
item.insert("used".to_string(), json!(usage.used));
|
||||
item.insert("free".to_string(), json!(usage.free));
|
||||
item.insert("percent".to_string(), json!((usage.percent * 10.0).round() / 10.0));
|
||||
item.insert(
|
||||
"percent".to_string(),
|
||||
json!((usage.percent * 10.0).round() / 10.0),
|
||||
);
|
||||
item.insert("key".to_string(), json!("mnt_point"));
|
||||
if let Some(alias) = self.model.has_alias(&mnt_point) {
|
||||
item.insert("alias".to_string(), json!(alias));
|
||||
@@ -96,7 +104,11 @@ impl Plugin for FsPlugin {
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
let mnt = item.get("mnt_point")?.as_str()?.to_string();
|
||||
let options = item.get("options").and_then(Value::as_str).unwrap_or("").to_string();
|
||||
let options = item
|
||||
.get("options")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let size = item.get("size")?.as_f64()?;
|
||||
let free = item.get("free")?.as_f64()?;
|
||||
Some((mnt, size, free, options))
|
||||
@@ -109,8 +121,18 @@ impl Plugin for FsPlugin {
|
||||
if options.split(',').any(|o| o == "ro") {
|
||||
continue;
|
||||
}
|
||||
let decoration = self.model.get_alert(size - free, 0.0, size, true, false, Some(&mnt_point), None, false);
|
||||
self.model.set_view_decoration(Some(&mnt_point), "used", decoration);
|
||||
let decoration = self.model.get_alert(
|
||||
size - free,
|
||||
0.0,
|
||||
size,
|
||||
true,
|
||||
false,
|
||||
Some(&mnt_point),
|
||||
None,
|
||||
false,
|
||||
);
|
||||
self.model
|
||||
.set_view_decoration(Some(&mnt_point), "used", decoration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +142,14 @@ impl Plugin for FsPlugin {
|
||||
|
||||
fn render(&mut self, ctx: &glances_core::RenderContext) -> Vec<glances_core::StatLine> {
|
||||
let mut ret = Vec::new();
|
||||
if self.model.stats.as_array().map(|a| a.is_empty()).unwrap_or(true) || ctx.max_width == 0 {
|
||||
if self
|
||||
.model
|
||||
.stats
|
||||
.as_array()
|
||||
.map(|a| a.is_empty())
|
||||
.unwrap_or(true)
|
||||
|| ctx.max_width == 0
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
let name_max_width = ctx.max_width.saturating_sub(13);
|
||||
@@ -132,30 +161,66 @@ impl Plugin for FsPlugin {
|
||||
false,
|
||||
));
|
||||
if ctx.runtime.fs_free_space {
|
||||
ret.push(self.model.curse_add_line(format!("{:>8}", "Free"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>8}", "Free"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
} else {
|
||||
ret.push(self.model.curse_add_line(format!("{:>8}", "Used"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>8}", "Used"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "Total"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "Total"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
|
||||
let Some(items) = self.model.stats.as_array() else {
|
||||
return ret;
|
||||
};
|
||||
let mut sorted = items.clone();
|
||||
sorted.sort_by_key(|i| i.get("mnt_point").and_then(Value::as_str).unwrap_or("").to_string());
|
||||
sorted.sort_by_key(|i| {
|
||||
i.get("mnt_point")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
});
|
||||
for i in &sorted {
|
||||
let mnt_point_raw = i.get("mnt_point").and_then(Value::as_str).unwrap_or("").to_string();
|
||||
let device = i.get("device_name").and_then(Value::as_str).unwrap_or("").to_string();
|
||||
let mnt_point_raw = i
|
||||
.get("mnt_point")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let device = i
|
||||
.get("device_name")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let mut mnt_point = i
|
||||
.get("alias")
|
||||
.and_then(Value::as_str)
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| mnt_point_raw.clone());
|
||||
let device_basename = device.rsplit('/').next().unwrap_or("").to_string();
|
||||
if mnt_point.chars().count() + device_basename.chars().count() <= name_max_width.saturating_sub(3) {
|
||||
if mnt_point.chars().count() + device_basename.chars().count()
|
||||
<= name_max_width.saturating_sub(3)
|
||||
{
|
||||
mnt_point = format!("{mnt_point} ({device_basename})");
|
||||
} else if mnt_point.chars().count() > name_max_width {
|
||||
mnt_point = format!("{}_", mnt_point.chars().take(name_max_width).collect::<String>());
|
||||
mnt_point = format!(
|
||||
"{}_",
|
||||
mnt_point.chars().take(name_max_width).collect::<String>()
|
||||
);
|
||||
}
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(
|
||||
|
||||
@@ -80,12 +80,15 @@ impl IpPlugin {
|
||||
if let (Some(username), Some(password)) = (&self.public_username, &self.public_password) {
|
||||
request = request.header("Authorization", &format!("Basic {}:{}", username, password));
|
||||
}
|
||||
if let Ok(mut response) = request.call() {
|
||||
if let Ok(value) = response.body_mut().read_json::<Value>() {
|
||||
self.public_address =
|
||||
value.get(&self.public_field).and_then(Value::as_str).unwrap_or("").to_string();
|
||||
self.public_info = Some(value);
|
||||
}
|
||||
if let Ok(mut response) = request.call()
|
||||
&& let Ok(value) = response.body_mut().read_json::<Value>()
|
||||
{
|
||||
self.public_address = value
|
||||
.get(&self.public_field)
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
self.public_info = Some(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +133,10 @@ impl Plugin for IpPlugin {
|
||||
if let Some(mask) = &mask {
|
||||
stats.insert("mask".to_string(), json!(mask));
|
||||
}
|
||||
stats.insert("mask_cidr".to_string(), json!(crate::collectors::ip_to_cidr(mask.as_deref())));
|
||||
stats.insert(
|
||||
"mask_cidr".to_string(),
|
||||
json!(crate::collectors::ip_to_cidr(mask.as_deref())),
|
||||
);
|
||||
self.refresh_public();
|
||||
if !self.public_address.is_empty() {
|
||||
let public = if ctx.args.hide_public_info {
|
||||
@@ -158,14 +164,26 @@ impl Plugin for IpPlugin {
|
||||
return ret;
|
||||
};
|
||||
let mut text = address.to_string();
|
||||
if let Some(mask_cidr) = s.get("mask_cidr").and_then(Value::as_u64) {
|
||||
if mask_cidr > 0 {
|
||||
text += &format!("/{mask_cidr}");
|
||||
}
|
||||
if let Some(mask_cidr) = s.get("mask_cidr").and_then(Value::as_u64)
|
||||
&& mask_cidr > 0
|
||||
{
|
||||
text += &format!("/{mask_cidr}");
|
||||
}
|
||||
ret.push(self.model.curse_add_line(text, glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
text,
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
if let Some(gateway) = s.get("gateway").and_then(Value::as_str) {
|
||||
ret.push(self.model.curse_add_line(format!(" gw {gateway}"), glances_core::Decoration::Default, true, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!(" gw {gateway}"),
|
||||
glances_core::Decoration::Default,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
@@ -28,7 +28,10 @@ impl IrqPlugin {
|
||||
args.disable_config_exec,
|
||||
);
|
||||
model.set_item_key("irq_line");
|
||||
Self { model, lasts: IndexMap::new() }
|
||||
Self {
|
||||
model,
|
||||
lasts: IndexMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +60,10 @@ impl Plugin for IrqPlugin {
|
||||
return Ok(Value::Array(stats));
|
||||
};
|
||||
let mut lines = content.lines();
|
||||
let cpu_number = lines.next().map(|header| header.split_whitespace().count()).unwrap_or(0);
|
||||
let cpu_number = lines
|
||||
.next()
|
||||
.map(|header| header.split_whitespace().count())
|
||||
.unwrap_or(0);
|
||||
for line in lines {
|
||||
let fields: Vec<&str> = line.split_whitespace().collect();
|
||||
if fields.len() < 2 {
|
||||
@@ -69,7 +75,10 @@ impl Plugin for IrqPlugin {
|
||||
} else {
|
||||
first.to_string()
|
||||
};
|
||||
let current: u64 = fields[1..1 + cpu_number].iter().filter_map(|v| v.parse::<u64>().ok()).sum();
|
||||
let current: u64 = fields[1..1 + cpu_number]
|
||||
.iter()
|
||||
.filter_map(|v| v.parse::<u64>().ok())
|
||||
.sum();
|
||||
let irq_rate = match self.lasts.get(&irq_line) {
|
||||
Some(last) if *last > 0 && time_since_update > 0.0 => {
|
||||
((current.saturating_sub(*last)) as f64 / time_since_update) as i64
|
||||
@@ -101,7 +110,14 @@ impl Plugin for IrqPlugin {
|
||||
|
||||
fn render(&mut self, ctx: &glances_core::RenderContext) -> Vec<glances_core::StatLine> {
|
||||
let mut ret = Vec::new();
|
||||
if self.model.stats.as_array().map(|a| a.is_empty()).unwrap_or(true) || ctx.max_width == 0 {
|
||||
if self
|
||||
.model
|
||||
.stats
|
||||
.as_array()
|
||||
.map(|a| a.is_empty())
|
||||
.unwrap_or(true)
|
||||
|| ctx.max_width == 0
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
let name_max_width = ctx.max_width.saturating_sub(7);
|
||||
@@ -112,7 +128,13 @@ impl Plugin for IrqPlugin {
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(format!("{:>9}", "Rate/s"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>9}", "Rate/s"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let Some(items) = self.model.stats.as_array() else {
|
||||
return ret;
|
||||
};
|
||||
@@ -121,13 +143,23 @@ impl Plugin for IrqPlugin {
|
||||
let rate = i.get("irq_rate").and_then(Value::as_i64).unwrap_or(0);
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<width$}", line.chars().take(name_max_width).collect::<String>(), width = name_max_width),
|
||||
format!(
|
||||
"{:<width$}",
|
||||
line.chars().take(name_max_width).collect::<String>(),
|
||||
width = name_max_width
|
||||
),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{rate:>9}"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(format!("{rate:>9}"), glances_core::Decoration::Default, false, false, false));
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
@@ -64,13 +64,21 @@ impl Plugin for LoadPlugin {
|
||||
fn update_views(&mut self) {
|
||||
let key = self.get_key();
|
||||
self.model.update_views(key);
|
||||
let max = self.model.stats.get("cpucore").and_then(Value::as_f64).unwrap_or(0.0) * 100.0;
|
||||
let max = self
|
||||
.model
|
||||
.stats
|
||||
.get("cpucore")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0)
|
||||
* 100.0;
|
||||
if let Some(min15) = self.model.stats.get("min15").and_then(Value::as_f64) {
|
||||
let decoration = self.model.get_alert_log(min15, max, None);
|
||||
self.model.set_view_decoration(None, "min15", decoration);
|
||||
}
|
||||
if let Some(min5) = self.model.stats.get("min5").and_then(Value::as_f64) {
|
||||
let decoration = self.model.get_alert(min5, 0.0, max, true, false, None, None, false);
|
||||
let decoration = self
|
||||
.model
|
||||
.get_alert(min5, 0.0, max, true, false, None, None, false);
|
||||
self.model.set_view_decoration(None, "min5", decoration);
|
||||
}
|
||||
}
|
||||
@@ -85,23 +93,47 @@ impl Plugin for LoadPlugin {
|
||||
if !s.is_object() || s.as_object().map(|o| o.is_empty()).unwrap_or(true) {
|
||||
return ret;
|
||||
}
|
||||
ret.push(self.model.curse_add_line(format!("{:4}", "LOAD"), glances_core::Decoration::Title, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!(" {:1}", self.model.trend_msg(self.model.get_trend("min1", 30), 1.0)),
|
||||
format!("{:4}", "LOAD"),
|
||||
glances_core::Decoration::Title,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!(
|
||||
" {:1}",
|
||||
self.model.trend_msg(self.model.get_trend("min1", 30), 1.0)
|
||||
),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
if let Some(cpucore) = s.get("cpucore").and_then(Value::as_f64) {
|
||||
if cpucore > 0.0 {
|
||||
ret.push(self.model.curse_add_line(format!("{:3}core", cpucore as i64), glances_core::Decoration::Default, false, false, false));
|
||||
}
|
||||
if let Some(cpucore) = s.get("cpucore").and_then(Value::as_f64)
|
||||
&& cpucore > 0.0
|
||||
{
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:3}core", cpucore as i64),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
for load_time in ["1", "5", "15"] {
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(format!("{:7}", format!("{load_time} min")), glances_core::Decoration::Default, false, false, false));
|
||||
let stat = s.get(&format!("min{load_time}")).and_then(Value::as_f64).unwrap_or(0.0);
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:7}", format!("{load_time} min")),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let stat = s
|
||||
.get(format!("min{load_time}"))
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
let value = if ctx.runtime.disable_irix {
|
||||
let cores = s.get("cpucore").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
if cores != 0.0 {
|
||||
|
||||
@@ -69,7 +69,11 @@ impl Plugin for MemPlugin {
|
||||
let buffers = get("Buffers");
|
||||
// procps-ng "free" sums reclaimable slab to cached.
|
||||
let cached = get("Cached") + get("SReclaimable");
|
||||
let shared = if meminfo.contains_key("Shmem") { get("Shmem") } else { get("MemShared") };
|
||||
let shared = if meminfo.contains_key("Shmem") {
|
||||
get("Shmem")
|
||||
} else {
|
||||
get("MemShared")
|
||||
};
|
||||
let active = get("Active");
|
||||
let inactive = get("Inactive");
|
||||
let mut available = get("MemAvailable");
|
||||
@@ -104,8 +108,18 @@ impl Plugin for MemPlugin {
|
||||
fn update_views(&mut self) {
|
||||
let key = self.get_key();
|
||||
self.model.update_views(key);
|
||||
let total = self.model.stats.get("total").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let used = self.model.stats.get("used").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let total = self
|
||||
.model
|
||||
.stats
|
||||
.get("total")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
let used = self
|
||||
.model
|
||||
.stats
|
||||
.get("used")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
let decoration = self.model.get_alert_log(used, total, None);
|
||||
self.model.set_view_decoration(None, "percent", decoration);
|
||||
}
|
||||
@@ -121,8 +135,20 @@ impl Plugin for MemPlugin {
|
||||
return ret;
|
||||
}
|
||||
// First line: total% + trend + active.
|
||||
ret.push(self.model.curse_add_line(format!("{}", "MEM"), glances_core::Decoration::Title, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!(" {:2}", self.model.trend_msg(self.model.get_trend("percent", 30), 1.0)), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
"MEM".to_string(),
|
||||
glances_core::Decoration::Title,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!(" {:2}", self.model.trend_msg(self.model.get_trend("percent", 30), 1.0)),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
if let Some(percent) = s.get("percent").and_then(Value::as_f64) {
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>6.1}%", percent),
|
||||
@@ -136,7 +162,10 @@ impl Plugin for MemPlugin {
|
||||
// Second line: total + inactive.
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.extend(self.model.curse_add_stat("total", Some(15), "", "", ""));
|
||||
ret.extend(self.model.curse_add_stat("inactive", Some(16), " ", "", ""));
|
||||
ret.extend(
|
||||
self.model
|
||||
.curse_add_stat("inactive", Some(16), " ", "", ""),
|
||||
);
|
||||
// Third line: available + buffers.
|
||||
ret.push(self.model.curse_new_line());
|
||||
if self.available {
|
||||
|
||||
@@ -53,7 +53,11 @@ impl Plugin for MemSwapPlugin {
|
||||
let total = meminfo.get("SwapTotal").copied().unwrap_or(0);
|
||||
let free = meminfo.get("SwapFree").copied().unwrap_or(0);
|
||||
let used = total.saturating_sub(free);
|
||||
let percent = if total > 0 { (used as f64 * 100.0 / total as f64 * 10.0).round() / 10.0 } else { 0.0 };
|
||||
let percent = if total > 0 {
|
||||
(used as f64 * 100.0 / total as f64 * 10.0).round() / 10.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let vmstat = crate::collectors::read_vmstat();
|
||||
// pswpin/pswpout are expressed in 4 KiB pages.
|
||||
let sin = vmstat.get("pswpin").copied().unwrap_or(0) * 4 * 1024;
|
||||
@@ -73,8 +77,18 @@ impl Plugin for MemSwapPlugin {
|
||||
fn update_views(&mut self) {
|
||||
let key = self.get_key();
|
||||
self.model.update_views(key);
|
||||
let total = self.model.stats.get("total").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let used = self.model.stats.get("used").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let total = self
|
||||
.model
|
||||
.stats
|
||||
.get("total")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
let used = self
|
||||
.model
|
||||
.stats
|
||||
.get("used")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
let decoration = self.model.get_alert_log(used, total, None);
|
||||
self.model.set_view_decoration(None, "percent", decoration);
|
||||
}
|
||||
@@ -89,10 +103,25 @@ impl Plugin for MemSwapPlugin {
|
||||
if !s.is_object() {
|
||||
return ret;
|
||||
}
|
||||
ret.push(self.model.curse_add_line("SWAP", glances_core::Decoration::Title, false, false, false));
|
||||
ret.push(self.model.curse_add_line(" ", glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>6.1}%", s.get("percent").and_then(Value::as_f64).unwrap_or(0.0)),
|
||||
"SWAP",
|
||||
glances_core::Decoration::Title,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
" ",
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!(
|
||||
"{:>6.1}%",
|
||||
s.get("percent").and_then(Value::as_f64).unwrap_or(0.0)
|
||||
),
|
||||
self.model.view_decoration(None, "percent"),
|
||||
false,
|
||||
false,
|
||||
|
||||
@@ -39,11 +39,19 @@ impl NetworkPlugin {
|
||||
);
|
||||
model.set_item_key("interface_name");
|
||||
model.hide_zero = config.get_bool_value("network", "hide_zero", false);
|
||||
model.hide_zero_fields =
|
||||
vec!["bytes_recv_rate_per_sec".to_string(), "bytes_sent_rate_per_sec".to_string()];
|
||||
let hide_no_up = config.has_section("network") && config.get_bool_value("network", "hide_no_up", false);
|
||||
let hide_no_ip = config.has_section("network") && config.get_bool_value("network", "hide_no_ip", false);
|
||||
Self { model, hide_no_up, hide_no_ip }
|
||||
model.hide_zero_fields = vec![
|
||||
"bytes_recv_rate_per_sec".to_string(),
|
||||
"bytes_sent_rate_per_sec".to_string(),
|
||||
];
|
||||
let hide_no_up =
|
||||
config.has_section("network") && config.get_bool_value("network", "hide_no_up", false);
|
||||
let hide_no_ip =
|
||||
config.has_section("network") && config.get_bool_value("network", "hide_no_ip", false);
|
||||
Self {
|
||||
model,
|
||||
hide_no_up,
|
||||
hide_no_ip,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +98,10 @@ impl Plugin for NetworkPlugin {
|
||||
item.insert("key".to_string(), json!("interface_name"));
|
||||
item.insert("interface_name".to_string(), json!(interface_name));
|
||||
item.insert("alias".to_string(), json!(alias));
|
||||
item.insert("bytes_all".to_string(), json!(counters.bytes_sent + counters.bytes_recv));
|
||||
item.insert(
|
||||
"bytes_all".to_string(),
|
||||
json!(counters.bytes_sent + counters.bytes_recv),
|
||||
);
|
||||
stats.push(Value::Object(item));
|
||||
}
|
||||
Ok(Value::Array(stats))
|
||||
@@ -104,8 +115,14 @@ impl Plugin for NetworkPlugin {
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
let name = item.get("interface_name")?.as_str()?.to_string();
|
||||
let rx = item.get("bytes_recv_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let tx = item.get("bytes_sent_rate_per_sec").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let rx = item
|
||||
.get("bytes_recv_rate_per_sec")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
let tx = item
|
||||
.get("bytes_sent_rate_per_sec")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
let speed = item.get("speed").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let real_name = name.split(':').next().unwrap_or("").to_string();
|
||||
Some((name, rx, tx, speed, Some(real_name)))
|
||||
@@ -117,21 +134,57 @@ impl Plugin for NetworkPlugin {
|
||||
let bps_rx = rx as i64 * 8;
|
||||
let bps_tx = tx as i64 * 8;
|
||||
let action_key = action_key.as_deref();
|
||||
let mut alert_rx =
|
||||
self.model.get_alert(bps_rx as f64, 0.0, 100.0, true, false, Some("rx"), action_key, false);
|
||||
let mut alert_tx =
|
||||
self.model.get_alert(bps_tx as f64, 0.0, 100.0, true, false, Some("tx"), action_key, false);
|
||||
let mut alert_rx = self.model.get_alert(
|
||||
bps_rx as f64,
|
||||
0.0,
|
||||
100.0,
|
||||
true,
|
||||
false,
|
||||
Some("rx"),
|
||||
action_key,
|
||||
false,
|
||||
);
|
||||
let mut alert_tx = self.model.get_alert(
|
||||
bps_tx as f64,
|
||||
0.0,
|
||||
100.0,
|
||||
true,
|
||||
false,
|
||||
Some("tx"),
|
||||
action_key,
|
||||
false,
|
||||
);
|
||||
if alert_rx == glances_core::Decoration::Default && speed != 0.0 {
|
||||
alert_rx = self.model.get_alert(bps_rx as f64, 0.0, speed, true, false, Some("rx"), None, false);
|
||||
alert_rx = self.model.get_alert(
|
||||
bps_rx as f64,
|
||||
0.0,
|
||||
speed,
|
||||
true,
|
||||
false,
|
||||
Some("rx"),
|
||||
None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
if alert_tx == glances_core::Decoration::Default && speed != 0.0 {
|
||||
alert_tx = self.model.get_alert(bps_tx as f64, 0.0, speed, true, false, Some("tx"), None, false);
|
||||
alert_tx = self.model.get_alert(
|
||||
bps_tx as f64,
|
||||
0.0,
|
||||
speed,
|
||||
true,
|
||||
false,
|
||||
Some("tx"),
|
||||
None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
for field in ["bytes_recv", "bytes_recv_rate_per_sec"] {
|
||||
self.model.set_view_decoration(Some(&interface_name), field, alert_rx);
|
||||
self.model
|
||||
.set_view_decoration(Some(&interface_name), field, alert_rx);
|
||||
}
|
||||
for field in ["bytes_sent", "bytes_sent_rate_per_sec"] {
|
||||
self.model.set_view_decoration(Some(&interface_name), field, alert_tx);
|
||||
self.model
|
||||
.set_view_decoration(Some(&interface_name), field, alert_tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,16 +209,52 @@ impl Plugin for NetworkPlugin {
|
||||
));
|
||||
if ctx.runtime.network_cumul {
|
||||
if ctx.runtime.network_sum {
|
||||
ret.push(self.model.curse_add_line(format!("{:>14}", "Rx+Tx"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>14}", "Rx+Tx"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
} else {
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "Rx"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "Tx"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "Rx"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "Tx"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
} else if ctx.runtime.network_sum {
|
||||
ret.push(self.model.curse_add_line(format!("{:>14}", "Rx+Tx/s"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>14}", "Rx+Tx/s"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
} else {
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "Rx/s"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "Tx/s"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "Rx/s"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "Tx/s"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
let stats = self.model.sorted_stats();
|
||||
@@ -173,7 +262,11 @@ impl Plugin for NetworkPlugin {
|
||||
return ret;
|
||||
};
|
||||
for i in items {
|
||||
let item_key = i.get("interface_name").and_then(Value::as_str).unwrap_or("").to_string();
|
||||
let item_key = i
|
||||
.get("interface_name")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
// Hide stats never different from zero.
|
||||
if self
|
||||
.model
|
||||
@@ -227,7 +320,13 @@ impl Plugin for NetworkPlugin {
|
||||
false,
|
||||
));
|
||||
if ctx.runtime.network_sum {
|
||||
ret.push(self.model.curse_add_line(format!("{ax:>14}"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{ax:>14}"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
} else {
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{rx:>7}"),
|
||||
|
||||
@@ -43,11 +43,15 @@ impl Plugin for NowPlugin {
|
||||
}
|
||||
|
||||
fn collect(&mut self, ctx: &mut UpdateContext) -> anyhow::Result<Value> {
|
||||
let iso = chrono::Local::now().format("%Y-%m-%dT%H:%M:%S%:z").to_string();
|
||||
let iso = chrono::Local::now()
|
||||
.format("%Y-%m-%dT%H:%M:%S%:z")
|
||||
.to_string();
|
||||
let strftime = if !ctx.args.strftime_format.is_empty() {
|
||||
ctx.args.strftime_format.clone()
|
||||
} else {
|
||||
ctx.config.get_value("global", "strftime_format", Some("")).unwrap_or_default()
|
||||
ctx.config
|
||||
.get_value("global", "strftime_format", Some(""))
|
||||
.unwrap_or_default()
|
||||
};
|
||||
let custom = if !strftime.is_empty() {
|
||||
crate::collectors::strftime(&strftime)
|
||||
@@ -72,7 +76,18 @@ impl Plugin for NowPlugin {
|
||||
}
|
||||
|
||||
fn render(&mut self, _ctx: &glances_core::RenderContext) -> Vec<glances_core::StatLine> {
|
||||
let custom = self.model.stats.get("custom").and_then(Value::as_str).unwrap_or("");
|
||||
vec![self.model.curse_add_line(format!("{custom:<23}"), glances_core::Decoration::Default, false, false, false)]
|
||||
let custom = self
|
||||
.model
|
||||
.stats
|
||||
.get("custom")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("");
|
||||
vec![self.model.curse_add_line(
|
||||
format!("{custom:<23}"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,10 @@ impl PerCpuPlugin {
|
||||
);
|
||||
model.set_item_key("cpu_number");
|
||||
let max_cpu_display = config.get_int_value("percpu", "max_cpu_display", 4).max(1) as usize;
|
||||
Self { model, max_cpu_display }
|
||||
Self {
|
||||
model,
|
||||
max_cpu_display,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,13 +78,27 @@ impl Plugin for PerCpuPlugin {
|
||||
}
|
||||
let items = &items;
|
||||
let quicklook_disabled = !ctx.runtime.is_plugin_enabled("quicklook");
|
||||
let mut header = vec!["user", "system", "iowait", "idle", "irq", "nice", "steal", "guest"];
|
||||
let mut header = vec![
|
||||
"user", "system", "iowait", "idle", "irq", "nice", "steal", "guest",
|
||||
];
|
||||
if quicklook_disabled {
|
||||
ret.push(self.model.curse_add_line(format!("{:5}", "CPU"), glances_core::Decoration::Title, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:5}", "CPU"),
|
||||
glances_core::Decoration::Title,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
header.insert(0, "total");
|
||||
}
|
||||
for stat in &header {
|
||||
ret.push(self.model.curse_add_line(format!("{stat:>7}"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{stat:>7}"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
// Sort by total and keep the top max_cpu_display cores.
|
||||
let mut sorted: Vec<Value> = items.clone();
|
||||
@@ -99,26 +116,54 @@ impl Plugin for PerCpuPlugin {
|
||||
ret.push(self.model.curse_new_line());
|
||||
if quicklook_disabled {
|
||||
let cpu_id = cpu.get("cpu_number").and_then(Value::as_i64).unwrap_or(0);
|
||||
let msg = if cpu_id < 10 { format!("CPU{cpu_id:1} ") } else { format!("{cpu_id:4} ") };
|
||||
ret.push(self.model.curse_add_line(msg, glances_core::Decoration::Default, false, false, false));
|
||||
let msg = if cpu_id < 10 {
|
||||
format!("CPU{cpu_id:1} ")
|
||||
} else {
|
||||
format!("{cpu_id:4} ")
|
||||
};
|
||||
ret.push(self.model.curse_add_line(
|
||||
msg,
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
for stat in &header {
|
||||
let value = cpu.get(*stat).and_then(Value::as_f64);
|
||||
let decoration = match value {
|
||||
Some(value) => self.model.get_alert(value, 0.0, 100.0, true, false, Some(stat), None, false),
|
||||
Some(value) => self.model.get_alert(
|
||||
value,
|
||||
0.0,
|
||||
100.0,
|
||||
true,
|
||||
false,
|
||||
Some(stat),
|
||||
None,
|
||||
false,
|
||||
),
|
||||
None => glances_core::Decoration::Default,
|
||||
};
|
||||
let msg = match value {
|
||||
Some(value) => format!("{value:6.1}%"),
|
||||
None => format!("{:>6}%", "?"),
|
||||
};
|
||||
ret.push(self.model.curse_add_line(msg, decoration, false, false, false));
|
||||
ret.push(
|
||||
self.model
|
||||
.curse_add_line(msg, decoration, false, false, false),
|
||||
);
|
||||
}
|
||||
}
|
||||
if items.len() > self.max_cpu_display {
|
||||
ret.push(self.model.curse_new_line());
|
||||
if quicklook_disabled {
|
||||
ret.push(self.model.curse_add_line("CPU* ", glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
"CPU* ",
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
for stat in &header {
|
||||
let values: Vec<f64> = sorted
|
||||
@@ -126,9 +171,21 @@ impl Plugin for PerCpuPlugin {
|
||||
.skip(self.max_cpu_display)
|
||||
.filter_map(|cpu| cpu.get(*stat).and_then(Value::as_f64))
|
||||
.collect();
|
||||
let mean = if values.is_empty() { 0.0 } else { values.iter().sum::<f64>() / values.len() as f64 };
|
||||
let decoration = self.model.get_alert(mean, 0.0, 100.0, true, false, Some(stat), None, false);
|
||||
ret.push(self.model.curse_add_line(format!("{mean:6.1}%"), decoration, false, false, false));
|
||||
let mean = if values.is_empty() {
|
||||
0.0
|
||||
} else {
|
||||
values.iter().sum::<f64>() / values.len() as f64
|
||||
};
|
||||
let decoration =
|
||||
self.model
|
||||
.get_alert(mean, 0.0, 100.0, true, false, Some(stat), None, false);
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{mean:6.1}%"),
|
||||
decoration,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
}
|
||||
ret
|
||||
|
||||
@@ -57,21 +57,21 @@ impl PortsPlugin {
|
||||
let mut entries = Vec::new();
|
||||
if config.has_section("ports") {
|
||||
// Default gateway entry.
|
||||
if config.get_bool_value("ports", "port_default_gateway", false) {
|
||||
if let Some(gateway) = crate::collectors::get_default_gateway() {
|
||||
entries.push(PortEntry {
|
||||
indice: "port_0".to_string(),
|
||||
host: gateway,
|
||||
port: None,
|
||||
url: None,
|
||||
description: "Default gateway".to_string(),
|
||||
refresh,
|
||||
timeout: default_timeout,
|
||||
rtt_warning: None,
|
||||
timer: Instant::now() - Duration::from_secs(3600),
|
||||
status: None,
|
||||
});
|
||||
}
|
||||
if config.get_bool_value("ports", "port_default_gateway", false)
|
||||
&& let Some(gateway) = crate::collectors::get_default_gateway()
|
||||
{
|
||||
entries.push(PortEntry {
|
||||
indice: "port_0".to_string(),
|
||||
host: gateway,
|
||||
port: None,
|
||||
url: None,
|
||||
description: "Default gateway".to_string(),
|
||||
refresh,
|
||||
timeout: default_timeout,
|
||||
rtt_warning: None,
|
||||
timer: Instant::now() - Duration::from_secs(3600),
|
||||
status: None,
|
||||
});
|
||||
}
|
||||
for index in 1..=255 {
|
||||
let key = format!("port_{index}_");
|
||||
@@ -81,14 +81,17 @@ impl PortsPlugin {
|
||||
if host.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let port = config.get_value("ports", &format!("{key}port"), None).and_then(|v| v.parse().ok());
|
||||
let port = config
|
||||
.get_value("ports", &format!("{key}port"), None)
|
||||
.and_then(|v| v.parse().ok());
|
||||
let description = config
|
||||
.get_value("ports", &format!("{key}description"), None)
|
||||
.unwrap_or_else(|| match port {
|
||||
Some(port) => format!("{host}:{port}"),
|
||||
None => host.clone(),
|
||||
});
|
||||
let timeout = config.get_float_value("ports", &format!("{key}timeout"), default_timeout);
|
||||
let timeout =
|
||||
config.get_float_value("ports", &format!("{key}timeout"), default_timeout);
|
||||
let rtt_warning = config
|
||||
.get_value("ports", &format!("{key}rtt_warning"), None)
|
||||
.and_then(|v| v.parse::<f64>().ok())
|
||||
@@ -114,8 +117,11 @@ impl PortsPlugin {
|
||||
if url.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let description = config.get_value("ports", &format!("{key}description"), None).unwrap_or_else(|| url.clone());
|
||||
let timeout = config.get_float_value("ports", &format!("{key}timeout"), default_timeout);
|
||||
let description = config
|
||||
.get_value("ports", &format!("{key}description"), None)
|
||||
.unwrap_or_else(|| url.clone());
|
||||
let timeout =
|
||||
config.get_float_value("ports", &format!("{key}timeout"), default_timeout);
|
||||
let rtt_warning = config
|
||||
.get_value("ports", &format!("{key}rtt_warning"), None)
|
||||
.and_then(|v| v.parse::<f64>().ok())
|
||||
@@ -134,14 +140,21 @@ impl PortsPlugin {
|
||||
});
|
||||
}
|
||||
}
|
||||
Self { model, entries, default_timeout }
|
||||
Self {
|
||||
model,
|
||||
entries,
|
||||
default_timeout,
|
||||
}
|
||||
}
|
||||
|
||||
fn check(entry: &PortEntry) -> Option<f64> {
|
||||
let start = Instant::now();
|
||||
if let Some(url) = &entry.url {
|
||||
let timeout = Duration::from_secs_f64(entry.timeout.max(0.1));
|
||||
let request = ureq::get(url).config().timeout_global(Some(timeout)).build();
|
||||
let request = ureq::get(url)
|
||||
.config()
|
||||
.timeout_global(Some(timeout))
|
||||
.build();
|
||||
match request.call() {
|
||||
Ok(_) => Some(start.elapsed().as_secs_f64()),
|
||||
Err(_) => None,
|
||||
@@ -158,7 +171,13 @@ impl PortsPlugin {
|
||||
} else {
|
||||
// ICMP check with the ping command.
|
||||
let output = std::process::Command::new("ping")
|
||||
.args(["-c", "1", "-W", &format!("{}", entry.timeout.max(1.0) as i64), &entry.host])
|
||||
.args([
|
||||
"-c",
|
||||
"1",
|
||||
"-W",
|
||||
&format!("{}", entry.timeout.max(1.0) as i64),
|
||||
&entry.host,
|
||||
])
|
||||
.output()
|
||||
.ok()?;
|
||||
let text = String::from_utf8_lossy(&output.stdout);
|
||||
@@ -237,10 +256,13 @@ impl Plugin for PortsPlugin {
|
||||
for (indice, status, warning) in items {
|
||||
let decoration = match status {
|
||||
None => glances_core::Decoration::Critical,
|
||||
Some(status) if warning.is_some_and(|w| status > w) => glances_core::Decoration::Warning,
|
||||
Some(status) if warning.is_some_and(|w| status > w) => {
|
||||
glances_core::Decoration::Warning
|
||||
}
|
||||
Some(_) => glances_core::Decoration::Ok,
|
||||
};
|
||||
self.model.set_view_decoration(Some(&indice), "status", decoration);
|
||||
self.model
|
||||
.set_view_decoration(Some(&indice), "status", decoration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +272,14 @@ impl Plugin for PortsPlugin {
|
||||
|
||||
fn render(&mut self, ctx: &glances_core::RenderContext) -> Vec<glances_core::StatLine> {
|
||||
let mut ret = Vec::new();
|
||||
if self.model.stats.as_array().map(|a| a.is_empty()).unwrap_or(true) || ctx.max_width == 0 {
|
||||
if self
|
||||
.model
|
||||
.stats
|
||||
.as_array()
|
||||
.map(|a| a.is_empty())
|
||||
.unwrap_or(true)
|
||||
|| ctx.max_width == 0
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
let name_max_width = ctx.max_width.saturating_sub(12);
|
||||
@@ -261,7 +290,13 @@ impl Plugin for PortsPlugin {
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(format!("{:>9}", "RTT"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>9}", "RTT"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let items = self.model.sorted_stats();
|
||||
let Some(items) = items.as_array() else {
|
||||
return ret;
|
||||
@@ -272,7 +307,11 @@ impl Plugin for PortsPlugin {
|
||||
let status = i.get("status").and_then(Value::as_f64);
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<width$}", description.chars().take(name_max_width).collect::<String>(), width = name_max_width),
|
||||
format!(
|
||||
"{:<width$}",
|
||||
description.chars().take(name_max_width).collect::<String>(),
|
||||
width = name_max_width
|
||||
),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
|
||||
@@ -77,50 +77,127 @@ impl Plugin for ProcessCountPlugin {
|
||||
return ret;
|
||||
}
|
||||
if ctx.args.disable_process {
|
||||
ret.push(self.model.curse_add_line("PROCESSES DISABLED (press 'z' to display)", glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
"PROCESSES DISABLED (press 'z' to display)",
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
return ret;
|
||||
}
|
||||
// Filter line.
|
||||
{
|
||||
let procs = glances_core::processes::processes().lock().unwrap();
|
||||
if let Some(filter) = procs.filter.filter() {
|
||||
ret.push(self.model.curse_add_line("Processes filter:", glances_core::Decoration::Title, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
"Processes filter:",
|
||||
glances_core::Decoration::Title,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let mut msg = format!(" {filter} ");
|
||||
if let Some(key) = procs.filter.filter_key() {
|
||||
msg += &format!("on column {key} ");
|
||||
}
|
||||
ret.push(self.model.curse_add_line(msg, glances_core::Decoration::Filter, false, false, false));
|
||||
ret.push(self.model.curse_add_line("('ENTER' to edit, 'E' to reset)", glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
msg,
|
||||
glances_core::Decoration::Filter,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
"('ENTER' to edit, 'E' to reset)",
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_new_line());
|
||||
}
|
||||
}
|
||||
ret.push(self.model.curse_add_line("TASKS", glances_core::Decoration::Title, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
"TASKS",
|
||||
glances_core::Decoration::Title,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let total = s.get("total").and_then(Value::as_i64).unwrap_or(0);
|
||||
ret.push(self.model.curse_add_line(format!("{total:>4}"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{total:>4}"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
if let Some(thread) = s.get("thread").and_then(Value::as_i64) {
|
||||
ret.push(self.model.curse_add_line(format!(" ({thread} thr),"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!(" ({thread} thr),"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
let mut other = total;
|
||||
if let Some(running) = s.get("running").and_then(Value::as_i64) {
|
||||
other -= running;
|
||||
ret.push(self.model.curse_add_line(format!(" {running} run,"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!(" {running} run,"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
if let Some(sleeping) = s.get("sleeping").and_then(Value::as_i64) {
|
||||
other -= sleeping;
|
||||
ret.push(self.model.curse_add_line(format!(" {sleeping} slp,"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!(" {sleeping} slp,"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
ret.push(self.model.curse_add_line(format!(" {other} oth "), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!(" {other} oth "),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let procs = glances_core::processes::processes().lock().unwrap();
|
||||
let mut msg = if ctx.args.programs { "Programs" } else { "Threads" }.to_string();
|
||||
let mut msg = if ctx.args.programs {
|
||||
"Programs"
|
||||
} else {
|
||||
"Threads"
|
||||
}
|
||||
.to_string();
|
||||
let sort_human = glances_core::processes::sort_for_human(&procs.sort_key);
|
||||
if procs.auto_sort {
|
||||
msg += " sorted automatically";
|
||||
ret.push(self.model.curse_add_line(msg, glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
msg,
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
msg = format!(" by {sort_human}");
|
||||
} else {
|
||||
msg += &format!(" sorted by {sort_human}");
|
||||
}
|
||||
ret.push(self.model.curse_add_line(msg, glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
msg,
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,33 +43,48 @@ impl ProcessListPlugin {
|
||||
model.set_item_key("pid");
|
||||
|
||||
// Load the configuration.
|
||||
if config.has_option("processlist", "sort_key") {
|
||||
if let Some(sort_key) = config.get_value("processlist", "sort_key", None) {
|
||||
let mut procs = processes().lock().unwrap();
|
||||
procs.set_sort_key(&sort_key, false);
|
||||
}
|
||||
if config.has_option("processlist", "sort_key")
|
||||
&& let Some(sort_key) = config.get_value("processlist", "sort_key", None)
|
||||
{
|
||||
let mut procs = processes().lock().unwrap();
|
||||
procs.set_sort_key(&sort_key, false);
|
||||
}
|
||||
if config.has_option("processlist", "focus") {
|
||||
if let Some(focus) = config.get_value("processlist", "focus", None) {
|
||||
processes().lock().unwrap().filter_focus.set_filter(Some(&focus));
|
||||
}
|
||||
if config.has_option("processlist", "focus")
|
||||
&& let Some(focus) = config.get_value("processlist", "focus", None)
|
||||
{
|
||||
processes()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.filter_focus
|
||||
.set_filter(Some(&focus));
|
||||
}
|
||||
if config.has_option("processlist", "disable_stats") {
|
||||
if let Some(disabled) = config.get_value("processlist", "disable_stats", None) {
|
||||
let stats: Vec<String> = disabled
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty() && !glances_core::processes::MANDATORY_PROCESS_STATS.contains(&s.as_str()))
|
||||
.collect();
|
||||
processes().lock().unwrap().disable_stats = stats;
|
||||
}
|
||||
if config.has_option("processlist", "disable_stats")
|
||||
&& let Some(disabled) = config.get_value("processlist", "disable_stats", None)
|
||||
{
|
||||
let stats: Vec<String> = disabled
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| {
|
||||
!s.is_empty()
|
||||
&& !glances_core::processes::MANDATORY_PROCESS_STATS.contains(&s.as_str())
|
||||
})
|
||||
.collect();
|
||||
processes().lock().unwrap().disable_stats = stats;
|
||||
}
|
||||
if let Some(sort_key) = args.sort_processes_key {
|
||||
processes().lock().unwrap().set_sort_key(sort_key.as_str(), false);
|
||||
processes()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_sort_key(sort_key.as_str(), false);
|
||||
}
|
||||
let nb_log_core = crate::collectors::logical_core_count();
|
||||
let disable_virtual_memory = config.get_bool_value("processlist", "disable_virtual_memory", false);
|
||||
Self { model, nb_log_core, disable_virtual_memory }
|
||||
let disable_virtual_memory =
|
||||
config.get_bool_value("processlist", "disable_virtual_memory", false);
|
||||
Self {
|
||||
model,
|
||||
nb_log_core,
|
||||
disable_virtual_memory,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +144,11 @@ impl Plugin for ProcessListPlugin {
|
||||
let procs = glances_core::processes::processes().lock().unwrap();
|
||||
(
|
||||
procs.max_values.get("cpu_percent").copied().unwrap_or(0.0),
|
||||
procs.max_values.get("memory_percent").copied().unwrap_or(0.0),
|
||||
procs
|
||||
.max_values
|
||||
.get("memory_percent")
|
||||
.copied()
|
||||
.unwrap_or(0.0),
|
||||
procs.sort_key.clone(),
|
||||
)
|
||||
};
|
||||
@@ -140,33 +159,120 @@ impl Plugin for ProcessListPlugin {
|
||||
|
||||
// Header.
|
||||
if ctx.runtime.disable_irix {
|
||||
ret.push(self.model.curse_add_line(format!("{:<6} ", "CPUi"), Decoration::Sort, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<6} ", "CPUi"),
|
||||
Decoration::Sort,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
} else {
|
||||
ret.push(self.model.curse_add_line(format!("{:<6} ", "CPU%"), Decoration::Sort, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<6} ", "CPU%"),
|
||||
Decoration::Sort,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
ret.push(self.model.curse_add_line(format!("{:<5} ", "MEM%"), Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<5} ", "MEM%"),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
if wide {
|
||||
if !self.disable_virtual_memory {
|
||||
ret.push(self.model.curse_add_line(format!("{:<5} ", "VIRT"), Decoration::Default, true, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<5} ", "VIRT"),
|
||||
Decoration::Default,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
ret.push(self.model.curse_add_line(format!("{:<5} ", "RES"), Decoration::Default, true, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<5} ", "RES"),
|
||||
Decoration::Default,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
ret.push(self.model.curse_add_line(format!("{:>5} ", "PID"), Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{:<10} ", "USER"), Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>5} ", "PID"),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<10} ", "USER"),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
if wide {
|
||||
ret.push(self.model.curse_add_line(format!("{:>8} ", "TIME+"), Decoration::Default, true, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>8} ", "TIME+"),
|
||||
Decoration::Default,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
ret.push(self.model.curse_add_line(format!("{:<3} ", "THR"), Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{:>3} ", "NI"), Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{:>1} ", "S"), Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<3} ", "THR"),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>3} ", "NI"),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>1} ", "S"),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
if wide {
|
||||
ret.push(self.model.curse_add_line(format!("{:>4} ", "R/s"), Decoration::Default, true, true, false));
|
||||
ret.push(self.model.curse_add_line(format!("{:<4} ", "W/s"), Decoration::Default, true, true, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>4} ", "R/s"),
|
||||
Decoration::Default,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<4} ", "W/s"),
|
||||
Decoration::Default,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
}
|
||||
if wide {
|
||||
ret.push(self.model.curse_add_line(format!("{:>3} ", "CPU"), Decoration::Default, true, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>3} ", "CPU"),
|
||||
Decoration::Default,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
ret.push(self.model.curse_add_line("Command", Decoration::Sort, false, false, false));
|
||||
ret.push(
|
||||
self.model
|
||||
.curse_add_line("Command", Decoration::Sort, false, false, false),
|
||||
);
|
||||
let _ = sort_key;
|
||||
|
||||
let max_pid_size = {
|
||||
@@ -181,7 +287,11 @@ impl Plugin for ProcessListPlugin {
|
||||
let selected = position as i64 == cursor;
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(
|
||||
if selected && !disable_cursor { ">" } else { " " },
|
||||
if selected && !disable_cursor {
|
||||
">"
|
||||
} else {
|
||||
" "
|
||||
},
|
||||
Decoration::Selected,
|
||||
false,
|
||||
false,
|
||||
@@ -205,10 +315,19 @@ impl Plugin for ProcessListPlugin {
|
||||
None,
|
||||
false,
|
||||
);
|
||||
ret.push(self.model.curse_add_line(format!("{cpu_value:<6.1}"), cpu_decoration, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{cpu_value:<6.1}"),
|
||||
cpu_decoration,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
|
||||
// MEM%.
|
||||
let memory_percent = p.get("memory_percent").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let memory_percent = p
|
||||
.get("memory_percent")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
let mem_decoration = self.model.get_alert(
|
||||
memory_percent,
|
||||
0.0,
|
||||
@@ -219,13 +338,22 @@ impl Plugin for ProcessListPlugin {
|
||||
None,
|
||||
false,
|
||||
);
|
||||
ret.push(self.model.curse_add_line(format!("{memory_percent:<5.1} "), mem_decoration, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{memory_percent:<5.1} "),
|
||||
mem_decoration,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
|
||||
// VIRT and RES.
|
||||
let memory_info = p.get("memory_info");
|
||||
if wide {
|
||||
if !self.disable_virtual_memory {
|
||||
let vms = memory_info.and_then(|m| m.get("vms")).and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let vms = memory_info
|
||||
.and_then(|m| m.get("vms"))
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<5} ", self.model.auto_unit(vms, false, "K")),
|
||||
Decoration::Default,
|
||||
@@ -234,7 +362,10 @@ impl Plugin for ProcessListPlugin {
|
||||
false,
|
||||
));
|
||||
}
|
||||
let rss = memory_info.and_then(|m| m.get("rss")).and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let rss = memory_info
|
||||
.and_then(|m| m.get("rss"))
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<5} ", self.model.auto_unit(rss, false, "K")),
|
||||
Decoration::Default,
|
||||
@@ -246,18 +377,36 @@ impl Plugin for ProcessListPlugin {
|
||||
|
||||
// PID.
|
||||
let pid = p.get("pid").and_then(Value::as_i64).unwrap_or(0);
|
||||
ret.push(self.model.curse_add_line(format!("{pid:>max_pid_size$} "), Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{pid:>max_pid_size$} "),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
|
||||
// USER.
|
||||
let username = p.get("username").and_then(Value::as_str).unwrap_or("?");
|
||||
let username: String = username.chars().take(9).collect();
|
||||
ret.push(self.model.curse_add_line(format!("{username:<10} "), Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{username:<10} "),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
|
||||
// TIME+.
|
||||
if wide {
|
||||
let times = p.get("cpu_times");
|
||||
let user = times.and_then(|t| t.get("user")).and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let system = times.and_then(|t| t.get("system")).and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let user = times
|
||||
.and_then(|t| t.get("user"))
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
let system = times
|
||||
.and_then(|t| t.get("system"))
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
let total = (user + system) as i64;
|
||||
let hours = total / 3600;
|
||||
let minutes = (total % 3600) / 60;
|
||||
@@ -265,29 +414,62 @@ impl Plugin for ProcessListPlugin {
|
||||
let (msg, decoration) = if hours > 99 {
|
||||
(format!("{hours:<7}h"), Decoration::Default)
|
||||
} else if hours > 0 {
|
||||
(format!("{hours}h{minutes}:{seconds:02}"), Decoration::CpuTime)
|
||||
(
|
||||
format!("{hours}h{minutes}:{seconds:02}"),
|
||||
Decoration::CpuTime,
|
||||
)
|
||||
} else {
|
||||
(format!("{minutes}:{seconds:02}"), Decoration::Default)
|
||||
};
|
||||
ret.push(self.model.curse_add_line(format!("{msg:>8} "), decoration, true, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{msg:>8} "),
|
||||
decoration,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
// THR.
|
||||
if let Some(threads) = p.get("num_threads").and_then(Value::as_i64) {
|
||||
ret.push(self.model.curse_add_line(format!("{threads:<3} "), Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{threads:<3} "),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
} else {
|
||||
ret.push(self.model.curse_add_line(format!("{:<3} ", "?"), Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<3} ", "?"),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
// NI.
|
||||
let nice_value = p.get("nice").and_then(Value::as_i64).unwrap_or(0);
|
||||
let nice_decoration = self.nice_alert(nice_value);
|
||||
ret.push(self.model.curse_add_line(format!("{nice_value:>3} "), nice_decoration, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{nice_value:>3} "),
|
||||
nice_decoration,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
|
||||
// Status.
|
||||
let status = p.get("status").and_then(Value::as_str).unwrap_or("?");
|
||||
let status_decoration = self.status_alert(status);
|
||||
ret.push(self.model.curse_add_line(format!("{status:>1} "), status_decoration, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{status:>1} "),
|
||||
status_decoration,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
|
||||
// IO rates.
|
||||
if wide {
|
||||
@@ -301,12 +483,28 @@ impl Plugin for ProcessListPlugin {
|
||||
let current = counters.get(slot)?.as_f64()?;
|
||||
let old = counters.get(if slot == 0 { 2 } else { 3 })?.as_f64()?;
|
||||
let io = (current - old) / time_since_update;
|
||||
Some(if io == 0.0 { "0".to_string() } else { self.model.auto_unit(io, true, "K") })
|
||||
Some(if io == 0.0 {
|
||||
"0".to_string()
|
||||
} else {
|
||||
self.model.auto_unit(io, true, "K")
|
||||
})
|
||||
};
|
||||
for (slot, width) in [(0usize, 4usize), (1, 4)] {
|
||||
match rate(slot) {
|
||||
Some(value) => ret.push(self.model.curse_add_line(format!("{value:>width$} "), Decoration::Default, true, true, false)),
|
||||
None => ret.push(self.model.curse_add_line(format!("{:>width$} ", "?"), Decoration::Default, true, true, false)),
|
||||
Some(value) => ret.push(self.model.curse_add_line(
|
||||
format!("{value:>width$} "),
|
||||
Decoration::Default,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
)),
|
||||
None => ret.push(self.model.curse_add_line(
|
||||
format!("{:>width$} ", "?"),
|
||||
Decoration::Default,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -318,7 +516,10 @@ impl Plugin for ProcessListPlugin {
|
||||
Some(value) if value >= 0 => format!("{value:>3} "),
|
||||
_ => format!("{:>3} ", "-"),
|
||||
};
|
||||
ret.push(self.model.curse_add_line(msg, Decoration::Default, true, false, false));
|
||||
ret.push(
|
||||
self.model
|
||||
.curse_add_line(msg, Decoration::Default, true, false, false),
|
||||
);
|
||||
}
|
||||
|
||||
// Command.
|
||||
@@ -326,9 +527,17 @@ impl Plugin for ProcessListPlugin {
|
||||
let cmdline: Vec<String> = p
|
||||
.get("cmdline")
|
||||
.and_then(Value::as_array)
|
||||
.map(|a| a.iter().map(|v| v.as_str().unwrap_or("").to_string()).collect())
|
||||
.map(|a| {
|
||||
a.iter()
|
||||
.map(|v| v.as_str().unwrap_or("").to_string())
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let process_decoration = if selected && !disable_cursor { Decoration::ProcessSelected } else { Decoration::Process };
|
||||
let process_decoration = if selected && !disable_cursor {
|
||||
Decoration::ProcessSelected
|
||||
} else {
|
||||
Decoration::Process
|
||||
};
|
||||
if !cmdline.is_empty() {
|
||||
let first = &cmdline[0];
|
||||
let (path, cmd) = if first.starts_with(name) {
|
||||
@@ -341,17 +550,50 @@ impl Plugin for ProcessListPlugin {
|
||||
};
|
||||
let arguments = cmdline[1..].join(" ");
|
||||
if !path.is_empty() && std::path::Path::new(&path).is_dir() && !short_name {
|
||||
ret.push(self.model.curse_add_line(format!("{path}/"), Decoration::Default, false, false, true));
|
||||
ret.push(self.model.curse_add_line(cmd, process_decoration, false, false, true));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{path}/"),
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
cmd,
|
||||
process_decoration,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
));
|
||||
} else {
|
||||
ret.push(self.model.curse_add_line(cmd, process_decoration, false, false, true));
|
||||
ret.push(self.model.curse_add_line(
|
||||
cmd,
|
||||
process_decoration,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
));
|
||||
}
|
||||
if !arguments.is_empty() {
|
||||
let msg = format!(" {}", glances_core::units::replace_special_chars(&arguments, ' '));
|
||||
ret.push(self.model.curse_add_line(msg, Decoration::Default, false, false, true));
|
||||
let msg = format!(
|
||||
" {}",
|
||||
glances_core::units::replace_special_chars(&arguments, ' ')
|
||||
);
|
||||
ret.push(self.model.curse_add_line(
|
||||
msg,
|
||||
Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
ret.push(self.model.curse_add_line(name.to_string(), process_decoration, false, false, true));
|
||||
ret.push(self.model.curse_add_line(
|
||||
name.to_string(),
|
||||
process_decoration,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
));
|
||||
}
|
||||
}
|
||||
ret
|
||||
@@ -367,10 +609,10 @@ impl ProcessListPlugin {
|
||||
("nice_careful", glances_core::Decoration::Careful),
|
||||
("nice_ok", glances_core::Decoration::Ok),
|
||||
] {
|
||||
if let Some(items) = self.model.get_limit_list(criticality, "processlist") {
|
||||
if items.iter().any(|item| item == &value) {
|
||||
return decoration;
|
||||
}
|
||||
if let Some(items) = self.model.get_limit_list(criticality, "processlist")
|
||||
&& items.iter().any(|item| item == &value)
|
||||
{
|
||||
return decoration;
|
||||
}
|
||||
}
|
||||
glances_core::Decoration::Default
|
||||
@@ -383,13 +625,17 @@ impl ProcessListPlugin {
|
||||
("status_careful", glances_core::Decoration::Careful),
|
||||
("status_ok", glances_core::Decoration::Ok),
|
||||
] {
|
||||
if let Some(items) = self.model.get_limit_list(criticality, "processlist") {
|
||||
if items.iter().any(|item| item == value) {
|
||||
return decoration;
|
||||
}
|
||||
if let Some(items) = self.model.get_limit_list(criticality, "processlist")
|
||||
&& items.iter().any(|item| item == value)
|
||||
{
|
||||
return decoration;
|
||||
}
|
||||
}
|
||||
if value == "R" { glances_core::Decoration::Ok } else { glances_core::Decoration::Default }
|
||||
if value == "R" {
|
||||
glances_core::Decoration::Ok
|
||||
} else {
|
||||
glances_core::Decoration::Default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,13 +51,23 @@ impl QuicklookPlugin {
|
||||
args.disable_config_exec,
|
||||
);
|
||||
let stats_list = model.get_conf_value("list", "");
|
||||
let mut stats_list: Vec<String> =
|
||||
if stats_list.is_empty() { DEFAULT_STATS_LIST.iter().map(|s| s.to_string()).collect() } else { stats_list };
|
||||
if stats_list.iter().any(|s| !AVAILABLE_STATS_LIST.contains(&s.as_str())) {
|
||||
let mut stats_list: Vec<String> = if stats_list.is_empty() {
|
||||
DEFAULT_STATS_LIST.iter().map(|s| s.to_string()).collect()
|
||||
} else {
|
||||
stats_list
|
||||
};
|
||||
if stats_list
|
||||
.iter()
|
||||
.any(|s| !AVAILABLE_STATS_LIST.contains(&s.as_str()))
|
||||
{
|
||||
stats_list = DEFAULT_STATS_LIST.iter().map(|s| s.to_string()).collect();
|
||||
}
|
||||
let max_cpu_display = config.get_int_value("percpu", "max_cpu_display", 4).max(0) as usize;
|
||||
Self { model, stats_list, max_cpu_display }
|
||||
Self {
|
||||
model,
|
||||
stats_list,
|
||||
max_cpu_display,
|
||||
}
|
||||
}
|
||||
|
||||
fn mhz_to_hz(value: Option<f64>) -> Option<f64> {
|
||||
@@ -88,7 +98,10 @@ impl Plugin for QuicklookPlugin {
|
||||
(info, cpu, percpu)
|
||||
};
|
||||
stats.insert("cpu_name".to_string(), json!(info.cpu_name));
|
||||
stats.insert("cpu_hz_current".to_string(), json!(Self::mhz_to_hz(info.cpu_hz_current)));
|
||||
stats.insert(
|
||||
"cpu_hz_current".to_string(),
|
||||
json!(Self::mhz_to_hz(info.cpu_hz_current)),
|
||||
);
|
||||
stats.insert("cpu_hz".to_string(), json!(Self::mhz_to_hz(info.cpu_hz)));
|
||||
stats.insert("cpu".to_string(), json!(cpu_value));
|
||||
stats.insert("percpu".to_string(), Value::Array(percpu_values));
|
||||
@@ -98,14 +111,19 @@ impl Plugin for QuicklookPlugin {
|
||||
let available = meminfo.get("MemAvailable").copied().unwrap_or(0) as f64;
|
||||
stats.insert(
|
||||
"mem".to_string(),
|
||||
json!(if total > 0.0 { ((total - available) / total * 100.0 * 10.0).round() / 10.0 } else { 0.0 }),
|
||||
json!(if total > 0.0 {
|
||||
((total - available) / total * 100.0 * 10.0).round() / 10.0
|
||||
} else {
|
||||
0.0
|
||||
}),
|
||||
);
|
||||
let swap_total = meminfo.get("SwapTotal").copied().unwrap_or(0);
|
||||
let swap_free = meminfo.get("SwapFree").copied().unwrap_or(0);
|
||||
stats.insert(
|
||||
"swap".to_string(),
|
||||
json!(if swap_total > 0 {
|
||||
(((swap_total - swap_free) as f64 * 100.0 / swap_total as f64) * 10.0).round() / 10.0
|
||||
(((swap_total - swap_free) as f64 * 100.0 / swap_total as f64) * 10.0).round()
|
||||
/ 10.0
|
||||
} else {
|
||||
0.0
|
||||
}),
|
||||
@@ -113,9 +131,16 @@ impl Plugin for QuicklookPlugin {
|
||||
|
||||
let log_core = crate::collectors::logical_core_count();
|
||||
stats.insert("cpu_log_core".to_string(), json!(log_core));
|
||||
stats.insert("cpu_phys_core".to_string(), json!(crate::collectors::physical_core_count()));
|
||||
stats.insert(
|
||||
"cpu_phys_core".to_string(),
|
||||
json!(crate::collectors::physical_core_count()),
|
||||
);
|
||||
let load = crate::collectors::read_loadavg().map(|(_, _, min15)| {
|
||||
if log_core > 0 { min15 / log_core as f64 * 100.0 } else { 0.0 }
|
||||
if log_core > 0 {
|
||||
min15 / log_core as f64 * 100.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
});
|
||||
stats.insert("load".to_string(), json!(load));
|
||||
stats.insert("gpu_mem".to_string(), Value::Null);
|
||||
@@ -130,8 +155,16 @@ impl Plugin for QuicklookPlugin {
|
||||
let stats_list = self.stats_list.clone();
|
||||
for stat_key in &stats_list {
|
||||
if let Some(value) = self.model.stats.get(stat_key).and_then(Value::as_f64) {
|
||||
let decoration =
|
||||
self.model.get_alert(value, 0.0, 100.0, true, false, Some(stat_key), None, false);
|
||||
let decoration = self.model.get_alert(
|
||||
value,
|
||||
0.0,
|
||||
100.0,
|
||||
true,
|
||||
false,
|
||||
Some(stat_key),
|
||||
None,
|
||||
false,
|
||||
);
|
||||
self.model.set_view_decoration(None, stat_key, decoration);
|
||||
}
|
||||
}
|
||||
@@ -142,22 +175,36 @@ impl Plugin for QuicklookPlugin {
|
||||
let mut percpu_decoration = serde_json::Map::new();
|
||||
if let Some(percpu) = self.model.stats.get("percpu").and_then(Value::as_array) {
|
||||
for cpu in percpu {
|
||||
let number = cpu.get("cpu_number").map(glances_core::py_str).unwrap_or_default();
|
||||
let number = cpu
|
||||
.get("cpu_number")
|
||||
.map(glances_core::py_str)
|
||||
.unwrap_or_default();
|
||||
let total = cpu.get("total").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
percpu_decoration.insert(number, json!(decoration_for_percent(&self.model, total).name()));
|
||||
percpu_decoration.insert(
|
||||
number,
|
||||
json!(decoration_for_percent(&self.model, total).name()),
|
||||
);
|
||||
}
|
||||
if percpu.len() > self.max_cpu_display && self.max_cpu_display > 0 {
|
||||
let mut hidden: Vec<f64> =
|
||||
percpu.iter().filter_map(|cpu| cpu.get("total").and_then(Value::as_f64)).collect();
|
||||
let mut hidden: Vec<f64> = percpu
|
||||
.iter()
|
||||
.filter_map(|cpu| cpu.get("total").and_then(Value::as_f64))
|
||||
.collect();
|
||||
hidden.sort_by(|a, b| b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal));
|
||||
let hidden = &hidden[self.max_cpu_display.min(hidden.len())..];
|
||||
if !hidden.is_empty() {
|
||||
let mean = hidden.iter().sum::<f64>() / hidden.len() as f64;
|
||||
percpu_decoration.insert("other".to_string(), json!(decoration_for_percent(&self.model, mean).name()));
|
||||
percpu_decoration.insert(
|
||||
"other".to_string(),
|
||||
json!(decoration_for_percent(&self.model, mean).name()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.model.views.insert("percpu_decoration".to_string(), Value::Object(percpu_decoration));
|
||||
self.model.views.insert(
|
||||
"percpu_decoration".to_string(),
|
||||
Value::Object(percpu_decoration),
|
||||
);
|
||||
}
|
||||
|
||||
fn display_curse(&self) -> bool {
|
||||
@@ -171,26 +218,45 @@ impl Plugin for QuicklookPlugin {
|
||||
return ret;
|
||||
}
|
||||
// System information: CPU name + frequency.
|
||||
let freq = match (s.get("cpu_hz_current").and_then(Value::as_f64), s.get("cpu_hz").and_then(Value::as_f64)) {
|
||||
let freq = match (
|
||||
s.get("cpu_hz_current").and_then(Value::as_f64),
|
||||
s.get("cpu_hz").and_then(Value::as_f64),
|
||||
) {
|
||||
(Some(current), Some(max)) => format!(" {:.2}/{:.2}GHz", current / 1e9, max / 1e9),
|
||||
(Some(current), None) => format!(" {:.2}GHz", current / 1e9),
|
||||
_ => String::new(),
|
||||
};
|
||||
let cpu_name = s.get("cpu_name").and_then(Value::as_str).unwrap_or("");
|
||||
let name = if (ctx.max_width as i64 - freq.len() as i64 + 7) > 0 {
|
||||
format!("{:<width$}", cpu_name, width = ctx.max_width - freq.len() + 7)
|
||||
format!(
|
||||
"{:<width$}",
|
||||
cpu_name,
|
||||
width = ctx.max_width - freq.len() + 7
|
||||
)
|
||||
} else if freq.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
"Frequency".to_string()
|
||||
};
|
||||
ret.push(self.model.curse_add_line(name.clone(), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(freq.clone(), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
name.clone(),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
freq.clone(),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_new_line());
|
||||
|
||||
let bar_size = std::cmp::max(
|
||||
name.len().saturating_add(freq.len()).saturating_sub(7),
|
||||
(ctx.max_width as usize).saturating_sub(9),
|
||||
ctx.max_width.saturating_sub(9),
|
||||
)
|
||||
.max(10);
|
||||
let bar_char = self
|
||||
@@ -201,14 +267,32 @@ impl Plugin for QuicklookPlugin {
|
||||
.unwrap_or_else(|| "|".to_string());
|
||||
|
||||
let mut lines: Vec<Vec<glances_core::StatLine>> = Vec::new();
|
||||
let mut push_line = |msg: String, bar_key: &str, percent: f64, decoration: glances_core::Decoration| {
|
||||
let mut push_line = |msg: String,
|
||||
bar_key: &str,
|
||||
percent: f64,
|
||||
decoration: glances_core::Decoration| {
|
||||
let bar = glances_core::units::bar_percent(percent, bar_size, &bar_char, true);
|
||||
lines.push(vec![
|
||||
self.model.curse_add_line(msg, glances_core::Decoration::Default, false, false, false),
|
||||
self.model.curse_add_line("[", glances_core::Decoration::Bold, false, false, false),
|
||||
self.model.curse_add_line(bar, decoration, false, false, false),
|
||||
self.model.curse_add_line("]", glances_core::Decoration::Bold, false, false, false),
|
||||
self.model.curse_add_line(" ", glances_core::Decoration::Default, false, false, false),
|
||||
self.model.curse_add_line(
|
||||
msg,
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
self.model
|
||||
.curse_add_line("[", glances_core::Decoration::Bold, false, false, false),
|
||||
self.model
|
||||
.curse_add_line(bar, decoration, false, false, false),
|
||||
self.model
|
||||
.curse_add_line("]", glances_core::Decoration::Bold, false, false, false),
|
||||
self.model.curse_add_line(
|
||||
" ",
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
]);
|
||||
let _ = bar_key;
|
||||
};
|
||||
@@ -230,7 +314,11 @@ impl Plugin for QuicklookPlugin {
|
||||
for cpu in sorted.iter().take(self.max_cpu_display) {
|
||||
let cpu_id = cpu.get("cpu_number").and_then(Value::as_i64).unwrap_or(0);
|
||||
let total = cpu.get("total").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let msg = if cpu_id < 10 { format!("{:<3}{cpu_id} ", key.to_uppercase()) } else { format!("{cpu_id:4} ") };
|
||||
let msg = if cpu_id < 10 {
|
||||
format!("{:<3}{cpu_id} ", key.to_uppercase())
|
||||
} else {
|
||||
format!("{cpu_id:4} ")
|
||||
};
|
||||
let decoration = self.decoration_for_percpu(cpu_id);
|
||||
push_line(msg, key, total, decoration);
|
||||
}
|
||||
@@ -240,14 +328,28 @@ impl Plugin for QuicklookPlugin {
|
||||
.skip(self.max_cpu_display)
|
||||
.filter_map(|cpu| cpu.get("total").and_then(Value::as_f64))
|
||||
.collect();
|
||||
let mean = if hidden.is_empty() { 0.0 } else { hidden.iter().sum::<f64>() / hidden.len() as f64 };
|
||||
let mean = if hidden.is_empty() {
|
||||
0.0
|
||||
} else {
|
||||
hidden.iter().sum::<f64>() / hidden.len() as f64
|
||||
};
|
||||
let decoration = self.decoration_for_percpu(-1);
|
||||
push_line(format!("{:<3}* ", key.to_uppercase()), key, mean, decoration);
|
||||
push_line(
|
||||
format!("{:<3}* ", key.to_uppercase()),
|
||||
key,
|
||||
mean,
|
||||
decoration,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if let Some(value) = s.get(key).and_then(Value::as_f64) {
|
||||
let decoration = self.model.view_decoration(None, key);
|
||||
push_line(format!("{:<4} ", key.to_uppercase()), key, value, decoration);
|
||||
push_line(
|
||||
format!("{:<4} ", key.to_uppercase()),
|
||||
key,
|
||||
value,
|
||||
decoration,
|
||||
);
|
||||
}
|
||||
}
|
||||
for line in lines {
|
||||
@@ -261,7 +363,11 @@ impl Plugin for QuicklookPlugin {
|
||||
|
||||
impl QuicklookPlugin {
|
||||
fn decoration_for_percpu(&self, cpu_id: i64) -> glances_core::Decoration {
|
||||
let key = if cpu_id < 0 { "other".to_string() } else { cpu_id.to_string() };
|
||||
let key = if cpu_id < 0 {
|
||||
"other".to_string()
|
||||
} else {
|
||||
cpu_id.to_string()
|
||||
};
|
||||
match self.model.get_view(None, "percpu_decoration", &key) {
|
||||
Value::String(name) => glances_core::Decoration::from_name(&name),
|
||||
_ => glances_core::Decoration::Default,
|
||||
|
||||
@@ -64,16 +64,18 @@ impl Plugin for RaidPlugin {
|
||||
let status = fields[2].to_string();
|
||||
let raid_type = fields
|
||||
.iter()
|
||||
.find(|f| f.starts_with("raid") || f.starts_with("linear") || f.starts_with("multipath"))
|
||||
.find(|f| {
|
||||
f.starts_with("raid") || f.starts_with("linear") || f.starts_with("multipath")
|
||||
})
|
||||
.map(|f| f.to_string())
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
// Devices (name[index]).
|
||||
let mut components = serde_json::Map::new();
|
||||
for field in fields.iter().skip(3) {
|
||||
if let Some(device) = field.split('[').next() {
|
||||
if !device.is_empty() {
|
||||
components.insert(device.to_string(), json!("active sync"));
|
||||
}
|
||||
if let Some(device) = field.split('[').next()
|
||||
&& !device.is_empty()
|
||||
{
|
||||
components.insert(device.to_string(), json!("active sync"));
|
||||
}
|
||||
}
|
||||
// The following line contains blocks and [used/available] [UU].
|
||||
@@ -81,30 +83,30 @@ impl Plugin for RaidPlugin {
|
||||
let mut available = components.len();
|
||||
if index + 1 < lines.len() {
|
||||
let detail = lines[index + 1];
|
||||
if let Some(start) = detail.rfind('[') {
|
||||
if let Some(end) = detail[start..].find(']') {
|
||||
let counts = &detail[start + 1..start + end];
|
||||
if let Some((u, a)) = counts.split_once('/') {
|
||||
used = u.parse().unwrap_or(used as i64) as usize;
|
||||
available = a.parse().unwrap_or(available as i64) as usize;
|
||||
}
|
||||
if let Some(start) = detail.rfind('[')
|
||||
&& let Some(end) = detail[start..].find(']')
|
||||
{
|
||||
let counts = &detail[start + 1..start + end];
|
||||
if let Some((u, a)) = counts.split_once('/') {
|
||||
used = u.parse().unwrap_or(used as i64) as usize;
|
||||
available = a.parse().unwrap_or(available as i64) as usize;
|
||||
}
|
||||
}
|
||||
if let Some(start) = detail.rfind('[') {
|
||||
if let Some(end) = detail[start..].find(']') {
|
||||
let flags = &detail[start + 1..start + end];
|
||||
if flags.len() == components.len() {
|
||||
let names: Vec<String> = components.keys().cloned().collect();
|
||||
for (position, flag) in flags.chars().enumerate() {
|
||||
if let Some(device) = names.get(position) {
|
||||
let status = match flag {
|
||||
'U' => "active sync",
|
||||
'_' => "faulty",
|
||||
'S' => "spare",
|
||||
_ => "unknown",
|
||||
};
|
||||
components.insert(device.clone(), json!(status));
|
||||
}
|
||||
if let Some(start) = detail.rfind('[')
|
||||
&& let Some(end) = detail[start..].find(']')
|
||||
{
|
||||
let flags = &detail[start + 1..start + end];
|
||||
if flags.len() == components.len() {
|
||||
let names: Vec<String> = components.keys().cloned().collect();
|
||||
for (position, flag) in flags.chars().enumerate() {
|
||||
if let Some(device) = names.get(position) {
|
||||
let status = match flag {
|
||||
'U' => "active sync",
|
||||
'_' => "faulty",
|
||||
'S' => "spare",
|
||||
_ => "unknown",
|
||||
};
|
||||
components.insert(device.clone(), json!(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,8 +148,20 @@ impl Plugin for RaidPlugin {
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "Used"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "Avail"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "Used"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "Avail"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let mut names: Vec<&String> = arrays.keys().collect();
|
||||
names.sort();
|
||||
for name in names {
|
||||
@@ -157,25 +171,48 @@ impl Plugin for RaidPlugin {
|
||||
let status = array.get("status").and_then(Value::as_str).unwrap_or("");
|
||||
let used = array.get("used").and_then(Value::as_i64).unwrap_or(0);
|
||||
let available = array.get("available").and_then(Value::as_i64).unwrap_or(0);
|
||||
let array_type = array.get("type").and_then(Value::as_str).unwrap_or("unknown");
|
||||
let array_type = array
|
||||
.get("type")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("unknown");
|
||||
let decoration = raid_alert(status, used, available, array_type);
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<width$}", name.chars().take(name_max_width).collect::<String>(), width = name_max_width),
|
||||
format!(
|
||||
"{:<width$}",
|
||||
name.chars().take(name_max_width).collect::<String>(),
|
||||
width = name_max_width
|
||||
),
|
||||
decoration,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(format!("{used:>7}"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(format!("{available:>7}"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{used:>7}"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{available:>7}"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn raid_alert(status: &str, used: i64, available: i64, array_type: &str) -> glances_core::Decoration {
|
||||
fn raid_alert(
|
||||
status: &str,
|
||||
used: i64,
|
||||
available: i64,
|
||||
array_type: &str,
|
||||
) -> glances_core::Decoration {
|
||||
if status != "active" || used < available {
|
||||
return glances_core::Decoration::Critical;
|
||||
}
|
||||
|
||||
@@ -49,27 +49,36 @@ fn read_hwmon() -> Vec<Value> {
|
||||
let name = file.file_name().to_string_lossy().to_string();
|
||||
let path = file.path();
|
||||
// Temperatures (millidegrees).
|
||||
if let Some(rest) = name.strip_prefix("temp") {
|
||||
if let Some(rest) = rest.strip_suffix("_input") {
|
||||
let Some(raw) = read_value(&path) else {
|
||||
continue;
|
||||
};
|
||||
let value = raw / 1000.0;
|
||||
let label = read_label(&dir, &format!("temp{rest}_label"), &format!("temp{rest}"));
|
||||
let warning = read_label_value(&dir, &format!("temp{rest}_max")).map(|v| v / 1000.0);
|
||||
let critical = read_label_value(&dir, &format!("temp{rest}_crit")).map(|v| v / 1000.0);
|
||||
stats.push(sensor_item(&label, "C", value, warning, critical, "temperature_core"));
|
||||
}
|
||||
if let Some(rest) = name.strip_prefix("temp")
|
||||
&& let Some(rest) = rest.strip_suffix("_input")
|
||||
{
|
||||
let Some(raw) = read_value(&path) else {
|
||||
continue;
|
||||
};
|
||||
let value = raw / 1000.0;
|
||||
let label = read_label(&dir, &format!("temp{rest}_label"), &format!("temp{rest}"));
|
||||
let warning =
|
||||
read_label_value(&dir, &format!("temp{rest}_max")).map(|v| v / 1000.0);
|
||||
let critical =
|
||||
read_label_value(&dir, &format!("temp{rest}_crit")).map(|v| v / 1000.0);
|
||||
stats.push(sensor_item(
|
||||
&label,
|
||||
"C",
|
||||
value,
|
||||
warning,
|
||||
critical,
|
||||
"temperature_core",
|
||||
));
|
||||
}
|
||||
// Fans (RPM).
|
||||
if let Some(rest) = name.strip_prefix("fan") {
|
||||
if let Some(rest) = rest.strip_suffix("_input") {
|
||||
let Some(value) = read_value(&path) else {
|
||||
continue;
|
||||
};
|
||||
let label = read_label(&dir, &format!("fan{rest}_label"), &format!("fan{rest}"));
|
||||
stats.push(sensor_item(&label, "R", value, None, None, "fan_speed"));
|
||||
}
|
||||
if let Some(rest) = name.strip_prefix("fan")
|
||||
&& let Some(rest) = rest.strip_suffix("_input")
|
||||
{
|
||||
let Some(value) = read_value(&path) else {
|
||||
continue;
|
||||
};
|
||||
let label = read_label(&dir, &format!("fan{rest}_label"), &format!("fan{rest}"));
|
||||
stats.push(sensor_item(&label, "R", value, None, None, "fan_speed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,7 +97,9 @@ fn read_batteries() -> Vec<Value> {
|
||||
let dir = entry.path();
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
let is_battery = name.starts_with("BAT")
|
||||
|| read_string(&dir.join("type")).map(|t| t == "Battery").unwrap_or(false);
|
||||
|| read_string(&dir.join("type"))
|
||||
.map(|t| t == "Battery")
|
||||
.unwrap_or(false);
|
||||
if !is_battery {
|
||||
continue;
|
||||
}
|
||||
@@ -97,14 +108,23 @@ fn read_batteries() -> Vec<Value> {
|
||||
};
|
||||
let mut item = sensor_item("Battery", "%", value, None, None, "battery");
|
||||
if let Some(status) = read_string(&dir.join("status")) {
|
||||
item.as_object_mut().unwrap().insert("status".to_string(), json!(status));
|
||||
item.as_object_mut()
|
||||
.unwrap()
|
||||
.insert("status".to_string(), json!(status));
|
||||
}
|
||||
stats.push(item);
|
||||
}
|
||||
stats
|
||||
}
|
||||
|
||||
fn sensor_item(label: &str, unit: &str, value: f64, warning: Option<f64>, critical: Option<f64>, r#type: &str) -> Value {
|
||||
fn sensor_item(
|
||||
label: &str,
|
||||
unit: &str,
|
||||
value: f64,
|
||||
warning: Option<f64>,
|
||||
critical: Option<f64>,
|
||||
r#type: &str,
|
||||
) -> Value {
|
||||
json!({
|
||||
"label": label,
|
||||
"unit": unit,
|
||||
@@ -117,7 +137,9 @@ fn sensor_item(label: &str, unit: &str, value: f64, warning: Option<f64>, critic
|
||||
}
|
||||
|
||||
fn read_string(path: &std::path::Path) -> Option<String> {
|
||||
std::fs::read_to_string(path).ok().map(|v| v.trim().to_string())
|
||||
std::fs::read_to_string(path)
|
||||
.ok()
|
||||
.map(|v| v.trim().to_string())
|
||||
}
|
||||
|
||||
fn read_value(path: &std::path::Path) -> Option<f64> {
|
||||
@@ -154,23 +176,40 @@ impl Plugin for SensorsPlugin {
|
||||
stats.extend(read_batteries());
|
||||
// Apply show/hide filters and aliases.
|
||||
stats.retain(|stat| {
|
||||
let label = stat.get("label").and_then(Value::as_str).unwrap_or("").to_lowercase();
|
||||
let label = stat
|
||||
.get("label")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
self.model.is_display(&label, "")
|
||||
});
|
||||
for stat in stats.iter_mut() {
|
||||
if let (Some(label), Some(r#type)) = (
|
||||
stat.get("label").and_then(Value::as_str).map(|s| s.to_lowercase()),
|
||||
stat.get("type").and_then(Value::as_str).map(|s| s.to_lowercase()),
|
||||
stat.get("label")
|
||||
.and_then(Value::as_str)
|
||||
.map(|s| s.to_lowercase()),
|
||||
stat.get("type")
|
||||
.and_then(Value::as_str)
|
||||
.map(|s| s.to_lowercase()),
|
||||
) {
|
||||
if let Some(alias) = self.model.has_alias(&format!("{}_{}", label, r#type)) {
|
||||
stat.as_object_mut().unwrap().insert("label".to_string(), json!(alias));
|
||||
stat.as_object_mut()
|
||||
.unwrap()
|
||||
.insert("label".to_string(), json!(alias));
|
||||
} else if let Some(alias) = self.model.has_alias(&label) {
|
||||
stat.as_object_mut().unwrap().insert("label".to_string(), json!(alias));
|
||||
stat.as_object_mut()
|
||||
.unwrap()
|
||||
.insert("label".to_string(), json!(alias));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort by label.
|
||||
stats.sort_by_key(|stat| stat.get("label").and_then(Value::as_str).unwrap_or("").to_lowercase());
|
||||
stats.sort_by_key(|stat| {
|
||||
stat.get("label")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_lowercase()
|
||||
});
|
||||
Ok(Value::Array(stats))
|
||||
}
|
||||
|
||||
@@ -196,14 +235,34 @@ impl Plugin for SensorsPlugin {
|
||||
if warning.is_some() || critical.is_some() {
|
||||
system_thresholds(value, warning, critical)
|
||||
} else {
|
||||
self.model.get_alert(value, 0.0, 100.0, true, false, Some(&label), Some(&r#type), false)
|
||||
self.model.get_alert(
|
||||
value,
|
||||
0.0,
|
||||
100.0,
|
||||
true,
|
||||
false,
|
||||
Some(&label),
|
||||
Some(&r#type),
|
||||
false,
|
||||
)
|
||||
}
|
||||
} else if r#type == "battery" {
|
||||
self.model.get_alert(100.0 - value, 0.0, 100.0, true, false, Some(&r#type), None, false)
|
||||
self.model.get_alert(
|
||||
100.0 - value,
|
||||
0.0,
|
||||
100.0,
|
||||
true,
|
||||
false,
|
||||
Some(&r#type),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
self.model.get_alert(value, 0.0, 100.0, true, false, Some(&r#type), None, false)
|
||||
self.model
|
||||
.get_alert(value, 0.0, 100.0, true, false, Some(&r#type), None, false)
|
||||
};
|
||||
self.model.set_view_decoration(Some(&label), "value", decoration);
|
||||
self.model
|
||||
.set_view_decoration(Some(&label), "value", decoration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,14 +287,22 @@ impl Plugin for SensorsPlugin {
|
||||
false,
|
||||
));
|
||||
for i in items {
|
||||
let item_key = i.get("label").and_then(Value::as_str).unwrap_or("").to_string();
|
||||
let item_key = i
|
||||
.get("label")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let label = i.get("label").and_then(Value::as_str).unwrap_or("");
|
||||
let sensor_type = i.get("type").and_then(Value::as_str).unwrap_or("");
|
||||
let unit = i.get("unit").and_then(Value::as_str).unwrap_or("");
|
||||
let value = i.get("value").and_then(Value::as_f64);
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<width$}", label.chars().take(name_max_width).collect::<String>(), width = name_max_width),
|
||||
format!(
|
||||
"{:<width$}",
|
||||
label.chars().take(name_max_width).collect::<String>(),
|
||||
width = name_max_width
|
||||
),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
@@ -244,11 +311,12 @@ impl Plugin for SensorsPlugin {
|
||||
let Some(value) = value else {
|
||||
continue;
|
||||
};
|
||||
let (value, unit) = if ctx.args.fahrenheit && sensor_type != "battery" && sensor_type != "fan_speed" {
|
||||
(glances_core::to_fahrenheit(value), "F")
|
||||
} else {
|
||||
(value, unit)
|
||||
};
|
||||
let (value, unit) =
|
||||
if ctx.args.fahrenheit && sensor_type != "battery" && sensor_type != "fan_speed" {
|
||||
(glances_core::to_fahrenheit(value), "F")
|
||||
} else {
|
||||
(value, unit)
|
||||
};
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>14}", format!("{value:.0}{unit}")),
|
||||
self.model.view_decoration(Some(&item_key), "value"),
|
||||
|
||||
@@ -85,9 +85,21 @@ impl Plugin for SystemPlugin {
|
||||
return ret;
|
||||
}
|
||||
let hostname = s.get("hostname").and_then(Value::as_str).unwrap_or("");
|
||||
ret.push(self.model.curse_add_line(hostname, glances_core::Decoration::Title, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
hostname,
|
||||
glances_core::Decoration::Title,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let hr_name = s.get("hr_name").and_then(Value::as_str).unwrap_or("");
|
||||
ret.push(self.model.curse_add_line(format!(" {hr_name} "), glances_core::Decoration::Default, true, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!(" {hr_name} "),
|
||||
glances_core::Decoration::Default,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +63,14 @@ impl Plugin for WifiPlugin {
|
||||
continue;
|
||||
}
|
||||
let ssid = fields[0].trim_end_matches(':').to_string();
|
||||
let quality_link = fields[2].trim_end_matches('.').parse::<f64>().unwrap_or(0.0);
|
||||
let quality_level = fields[3].trim_end_matches('.').parse::<f64>().unwrap_or(0.0);
|
||||
let quality_link = fields[2]
|
||||
.trim_end_matches('.')
|
||||
.parse::<f64>()
|
||||
.unwrap_or(0.0);
|
||||
let quality_level = fields[3]
|
||||
.trim_end_matches('.')
|
||||
.parse::<f64>()
|
||||
.unwrap_or(0.0);
|
||||
stats.push(json!({
|
||||
"key": "ssid",
|
||||
"ssid": ssid,
|
||||
@@ -105,7 +111,8 @@ impl Plugin for WifiPlugin {
|
||||
} else {
|
||||
Decoration::Ok
|
||||
};
|
||||
self.model.set_view_decoration(Some(&ssid), "quality_level", decoration);
|
||||
self.model
|
||||
.set_view_decoration(Some(&ssid), "quality_level", decoration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +122,14 @@ impl Plugin for WifiPlugin {
|
||||
|
||||
fn render(&mut self, ctx: &glances_core::RenderContext) -> Vec<glances_core::StatLine> {
|
||||
let mut ret = Vec::new();
|
||||
if self.model.stats.as_array().map(|a| a.is_empty()).unwrap_or(true) || ctx.max_width == 0 {
|
||||
if self
|
||||
.model
|
||||
.stats
|
||||
.as_array()
|
||||
.map(|a| a.is_empty())
|
||||
.unwrap_or(true)
|
||||
|| ctx.max_width == 0
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
let if_name_max_width = ctx.max_width.saturating_sub(5);
|
||||
@@ -126,16 +140,29 @@ impl Plugin for WifiPlugin {
|
||||
false,
|
||||
false,
|
||||
));
|
||||
ret.push(self.model.curse_add_line(format!("{:>7}", "dBm"), glances_core::Decoration::Default, false, false, false));
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:>7}", "dBm"),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
let Some(items) = self.model.stats.as_array() else {
|
||||
return ret;
|
||||
};
|
||||
for i in items {
|
||||
let ssid = i.get("ssid").and_then(Value::as_str).unwrap_or("");
|
||||
let level = i.get("quality_level").and_then(Value::as_f64).unwrap_or(0.0);
|
||||
let level = i
|
||||
.get("quality_level")
|
||||
.and_then(Value::as_f64)
|
||||
.unwrap_or(0.0);
|
||||
ret.push(self.model.curse_new_line());
|
||||
ret.push(self.model.curse_add_line(
|
||||
format!("{:<width$}", ssid.chars().take(if_name_max_width).collect::<String>(), width = if_name_max_width),
|
||||
format!(
|
||||
"{:<width$}",
|
||||
ssid.chars().take(if_name_max_width).collect::<String>(),
|
||||
width = if_name_max_width
|
||||
),
|
||||
glances_core::Decoration::Default,
|
||||
false,
|
||||
false,
|
||||
|
||||
Reference in New Issue
Block a user