Update version to 0.1.22 and fix system metric status calculation
All checks were successful
Build and Release / build-and-release (push) Successful in 1m11s
All checks were successful
Build and Release / build-and-release (push) Successful in 1m11s
- Fix /tmp usage status to use proper thresholds instead of hardcoded Ok status - Fix wear level status to use configurable thresholds instead of hardcoded values - Add dedicated tmp_status field to SystemWidget for proper /tmp status display - Remove host-level hourglass icon during service operations - Implement immediate service status updates after start/stop/restart commands - Remove active users display and collection from NixOS section - Fix immediate host status aggregation transmission to dashboard
This commit is contained in:
parent
43242debce
commit
2910b7d875
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -270,7 +270,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||
|
||||
[[package]]
|
||||
name = "cm-dashboard"
|
||||
version = "0.1.20"
|
||||
version = "0.1.21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -291,7 +291,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cm-dashboard-agent"
|
||||
version = "0.1.20"
|
||||
version = "0.1.21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -314,7 +314,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cm-dashboard-shared"
|
||||
version = "0.1.20"
|
||||
version = "0.1.21"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cm-dashboard-agent"
|
||||
version = "0.1.21"
|
||||
version = "0.1.22"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -270,7 +270,7 @@ impl Agent {
|
||||
}
|
||||
|
||||
/// Handle systemd service control commands
|
||||
async fn handle_service_control(&self, service_name: &str, action: &ServiceAction) -> Result<()> {
|
||||
async fn handle_service_control(&mut self, service_name: &str, action: &ServiceAction) -> Result<()> {
|
||||
let action_str = match action {
|
||||
ServiceAction::Start => "start",
|
||||
ServiceAction::Stop => "stop",
|
||||
@ -300,9 +300,12 @@ impl Agent {
|
||||
|
||||
// Force refresh metrics after service control to update service status
|
||||
if matches!(action, ServiceAction::Start | ServiceAction::Stop | ServiceAction::Restart) {
|
||||
info!("Triggering metric refresh after service control");
|
||||
// Note: We can't call self.collect_metrics_only() here due to borrowing issues
|
||||
// The next metric collection cycle will pick up the changes
|
||||
info!("Triggering immediate metric refresh after service control");
|
||||
if let Err(e) = self.collect_metrics_only().await {
|
||||
error!("Failed to refresh metrics after service control: {}", e);
|
||||
} else {
|
||||
info!("Service status refreshed immediately after {} {}", action_str, service_name);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -556,8 +556,8 @@ impl Collector for DiskCollector {
|
||||
|
||||
// Drive wear level (for SSDs)
|
||||
if let Some(wear) = drive.wear_level {
|
||||
let wear_status = if wear >= 90.0 { Status::Critical }
|
||||
else if wear >= 80.0 { Status::Warning }
|
||||
let wear_status = if wear >= self.config.wear_critical_percent { Status::Critical }
|
||||
else if wear >= self.config.wear_warning_percent { Status::Warning }
|
||||
else { Status::Ok };
|
||||
|
||||
metrics.push(Metric {
|
||||
|
||||
@ -187,7 +187,7 @@ impl MemoryCollector {
|
||||
}
|
||||
|
||||
// Monitor tmpfs (/tmp) usage
|
||||
if let Ok(tmpfs_metrics) = self.get_tmpfs_metrics() {
|
||||
if let Ok(tmpfs_metrics) = self.get_tmpfs_metrics(status_tracker) {
|
||||
metrics.extend(tmpfs_metrics);
|
||||
}
|
||||
|
||||
@ -195,7 +195,7 @@ impl MemoryCollector {
|
||||
}
|
||||
|
||||
/// Get tmpfs (/tmp) usage metrics
|
||||
fn get_tmpfs_metrics(&self) -> Result<Vec<Metric>, CollectorError> {
|
||||
fn get_tmpfs_metrics(&self, status_tracker: &mut StatusTracker) -> Result<Vec<Metric>, CollectorError> {
|
||||
use std::process::Command;
|
||||
|
||||
let output = Command::new("df")
|
||||
@ -249,12 +249,15 @@ impl MemoryCollector {
|
||||
let mut metrics = Vec::new();
|
||||
let timestamp = chrono::Utc::now().timestamp() as u64;
|
||||
|
||||
// Calculate status using same thresholds as main memory
|
||||
let tmp_status = self.calculate_usage_status("memory_tmp_usage_percent", usage_percent, status_tracker);
|
||||
|
||||
metrics.push(Metric {
|
||||
name: "memory_tmp_usage_percent".to_string(),
|
||||
value: MetricValue::Float(usage_percent),
|
||||
unit: Some("%".to_string()),
|
||||
description: Some("tmpfs /tmp usage percentage".to_string()),
|
||||
status: Status::Ok,
|
||||
status: tmp_status,
|
||||
timestamp,
|
||||
});
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@ use crate::config::NixOSConfig;
|
||||
///
|
||||
/// Collects NixOS-specific system information including:
|
||||
/// - NixOS version and build information
|
||||
/// - Currently active/logged in users
|
||||
pub struct NixOSCollector {
|
||||
}
|
||||
|
||||
@ -65,27 +64,6 @@ impl NixOSCollector {
|
||||
Err("Could not extract hash from nix store path".into())
|
||||
}
|
||||
|
||||
/// Get currently active users
|
||||
fn get_active_users(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
|
||||
let output = Command::new("who").output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err("who command failed".into());
|
||||
}
|
||||
|
||||
let who_output = String::from_utf8_lossy(&output.stdout);
|
||||
let mut users = std::collections::HashSet::new();
|
||||
|
||||
for line in who_output.lines() {
|
||||
if let Some(username) = line.split_whitespace().next() {
|
||||
if !username.is_empty() {
|
||||
users.insert(username.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(users.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -121,31 +99,6 @@ impl Collector for NixOSCollector {
|
||||
}
|
||||
}
|
||||
|
||||
// Collect active users
|
||||
match self.get_active_users() {
|
||||
Ok(users) => {
|
||||
let users_str = users.join(", ");
|
||||
metrics.push(Metric {
|
||||
name: "system_active_users".to_string(),
|
||||
value: MetricValue::String(users_str),
|
||||
unit: None,
|
||||
description: Some("Currently active users".to_string()),
|
||||
status: Status::Ok,
|
||||
timestamp,
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Failed to get active users: {}", e);
|
||||
metrics.push(Metric {
|
||||
name: "system_active_users".to_string(),
|
||||
value: MetricValue::String("unknown".to_string()),
|
||||
unit: None,
|
||||
description: Some("Active users (failed to detect)".to_string()),
|
||||
status: Status::Unknown,
|
||||
timestamp,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Collect config hash
|
||||
match self.get_config_hash() {
|
||||
|
||||
@ -160,27 +160,37 @@ impl HostStatusManager {
|
||||
|
||||
/// Process a metric - updates status and queues for aggregated notifications if status changed
|
||||
pub async fn process_metric(&mut self, metric: &Metric, _notification_manager: &mut crate::notifications::NotificationManager) -> bool {
|
||||
let old_status = self.service_statuses.get(&metric.name).copied();
|
||||
let new_status = metric.status;
|
||||
let old_service_status = self.service_statuses.get(&metric.name).copied();
|
||||
let old_host_status = self.current_host_status;
|
||||
let new_service_status = metric.status;
|
||||
|
||||
// Update status
|
||||
self.update_service_status(metric.name.clone(), new_status);
|
||||
// Update status (this recalculates host status internally)
|
||||
self.update_service_status(metric.name.clone(), new_service_status);
|
||||
|
||||
// Check if status actually changed (ignore first-time status setting)
|
||||
if let Some(old_status) = old_status {
|
||||
if old_status != new_status {
|
||||
debug!("Status change detected for {}: {:?} -> {:?}", metric.name, old_status, new_status);
|
||||
let new_host_status = self.current_host_status;
|
||||
let mut status_changed = false;
|
||||
|
||||
// Check if service status actually changed (ignore first-time status setting)
|
||||
if let Some(old_service_status) = old_service_status {
|
||||
if old_service_status != new_service_status {
|
||||
debug!("Service status change detected for {}: {:?} -> {:?}", metric.name, old_service_status, new_service_status);
|
||||
|
||||
// Queue change for aggregated notification (not immediate)
|
||||
self.queue_status_change(&metric.name, old_status, new_status);
|
||||
self.queue_status_change(&metric.name, old_service_status, new_service_status);
|
||||
|
||||
return true; // Status changed - caller should trigger immediate transmission
|
||||
status_changed = true;
|
||||
}
|
||||
} else {
|
||||
debug!("Initial status set for {}: {:?}", metric.name, new_status);
|
||||
debug!("Initial status set for {}: {:?}", metric.name, new_service_status);
|
||||
}
|
||||
|
||||
false // No status change (or first-time status)
|
||||
// Check if host status changed (this should trigger immediate transmission)
|
||||
if old_host_status != new_host_status {
|
||||
debug!("Host status change detected: {:?} -> {:?}", old_host_status, new_host_status);
|
||||
status_changed = true;
|
||||
}
|
||||
|
||||
status_changed // Return true if either service or host status changed
|
||||
}
|
||||
|
||||
/// Queue status change for aggregated notification
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cm-dashboard"
|
||||
version = "0.1.21"
|
||||
version = "0.1.22"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -14,7 +14,7 @@ use app::Dashboard;
|
||||
|
||||
/// Get hardcoded version
|
||||
fn get_version() -> &'static str {
|
||||
"v0.1.21"
|
||||
"v0.1.22"
|
||||
}
|
||||
|
||||
/// Check if running inside tmux session
|
||||
|
||||
@ -724,24 +724,9 @@ impl TuiApp {
|
||||
spans.push(Span::styled(" ", Typography::title()));
|
||||
}
|
||||
|
||||
// Check if this host has a command status that affects the icon
|
||||
let (status_icon, status_color) = if let Some(host_widgets) = self.host_widgets.get(host) {
|
||||
match &host_widgets.command_status {
|
||||
Some(CommandStatus::InProgress { .. }) => {
|
||||
// Show working indicator for in-progress commands
|
||||
("⏳", Theme::highlight())
|
||||
}
|
||||
_ => {
|
||||
// Normal status icon based on metrics
|
||||
let host_status = self.calculate_host_status(host, metric_store);
|
||||
(StatusIcons::get_icon(host_status), Theme::status_color(host_status))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No host widgets yet, use normal status
|
||||
let host_status = self.calculate_host_status(host, metric_store);
|
||||
(StatusIcons::get_icon(host_status), Theme::status_color(host_status))
|
||||
};
|
||||
// Always show normal status icon based on metrics (no command status at host level)
|
||||
let host_status = self.calculate_host_status(host, metric_store);
|
||||
let (status_icon, status_color) = (StatusIcons::get_icon(host_status), Theme::status_color(host_status));
|
||||
|
||||
// Add status icon
|
||||
spans.push(Span::styled(
|
||||
|
||||
@ -15,7 +15,6 @@ pub struct SystemWidget {
|
||||
// NixOS information
|
||||
nixos_build: Option<String>,
|
||||
config_hash: Option<String>,
|
||||
active_users: Option<String>,
|
||||
agent_hash: Option<String>,
|
||||
|
||||
// CPU metrics
|
||||
@ -33,6 +32,7 @@ pub struct SystemWidget {
|
||||
tmp_used_gb: Option<f32>,
|
||||
tmp_total_gb: Option<f32>,
|
||||
memory_status: Status,
|
||||
tmp_status: Status,
|
||||
|
||||
// Storage metrics (collected from disk metrics)
|
||||
storage_pools: Vec<StoragePool>,
|
||||
@ -66,7 +66,6 @@ impl SystemWidget {
|
||||
Self {
|
||||
nixos_build: None,
|
||||
config_hash: None,
|
||||
active_users: None,
|
||||
agent_hash: None,
|
||||
cpu_load_1min: None,
|
||||
cpu_load_5min: None,
|
||||
@ -80,6 +79,7 @@ impl SystemWidget {
|
||||
tmp_used_gb: None,
|
||||
tmp_total_gb: None,
|
||||
memory_status: Status::Unknown,
|
||||
tmp_status: Status::Unknown,
|
||||
storage_pools: Vec::new(),
|
||||
has_data: false,
|
||||
}
|
||||
@ -334,11 +334,6 @@ impl Widget for SystemWidget {
|
||||
self.config_hash = Some(hash.clone());
|
||||
}
|
||||
}
|
||||
"system_active_users" => {
|
||||
if let MetricValue::String(users) = &metric.value {
|
||||
self.active_users = Some(users.clone());
|
||||
}
|
||||
}
|
||||
"agent_version" => {
|
||||
if let MetricValue::String(version) = &metric.value {
|
||||
self.agent_hash = Some(version.clone());
|
||||
@ -390,6 +385,7 @@ impl Widget for SystemWidget {
|
||||
"memory_tmp_usage_percent" => {
|
||||
if let MetricValue::Float(usage) = metric.value {
|
||||
self.tmp_usage_percent = Some(usage);
|
||||
self.tmp_status = metric.status.clone();
|
||||
}
|
||||
}
|
||||
"memory_tmp_used_gb" => {
|
||||
@ -432,10 +428,6 @@ impl SystemWidget {
|
||||
Span::styled(format!("Agent: {}", agent_version_text), Typography::secondary())
|
||||
]));
|
||||
|
||||
let users_text = self.active_users.as_deref().unwrap_or("unknown");
|
||||
lines.push(Line::from(vec![
|
||||
Span::styled(format!("Active users: {}", users_text), Typography::secondary())
|
||||
]));
|
||||
|
||||
// CPU section
|
||||
lines.push(Line::from(vec![
|
||||
@ -472,7 +464,7 @@ impl SystemWidget {
|
||||
Span::styled(" └─ ", Typography::tree()),
|
||||
];
|
||||
tmp_spans.extend(StatusIcons::create_status_spans(
|
||||
self.memory_status.clone(),
|
||||
self.tmp_status.clone(),
|
||||
&format!("/tmp: {}", tmp_text)
|
||||
));
|
||||
lines.push(Line::from(tmp_spans));
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cm-dashboard-shared"
|
||||
version = "0.1.21"
|
||||
version = "0.1.22"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user