Clean up warnings and add Status::Pending support to dashboard UI

This commit is contained in:
Christoffer Martinsson 2025-10-21 18:27:11 +02:00
parent 41208aa2a0
commit 98e3ecb0ea
5 changed files with 11 additions and 126 deletions

View File

@ -61,46 +61,6 @@ impl NotificationManager {
})
}
/// Update metric status and return status change if any
pub fn update_metric_status(
&mut self,
metric_name: &str,
new_status: Status,
) -> Option<StatusChange> {
let old_status = self
.metric_statuses
.get(metric_name)
.copied()
.unwrap_or(Status::Unknown);
// Check if status actually changed
if old_status != new_status {
// Update stored status only on change
self.metric_statuses
.insert(metric_name.to_string(), new_status);
// Save status to disk only when status changes
self.save_status();
debug!(
"Status change detected for {}: {:?} -> {:?}",
metric_name, old_status, new_status
);
Some(StatusChange {
metric_name: metric_name.to_string(),
old_status,
new_status,
timestamp: Utc::now(),
details: None, // Will be populated when needed
})
} else {
// No status change - update stored status but don't save to disk
self.metric_statuses
.insert(metric_name.to_string(), new_status);
None
}
}
/// Send notification for status change
pub async fn send_status_change_notification(
@ -249,11 +209,6 @@ impl NotificationManager {
Ok(())
}
/// Process any pending notifications (placeholder)
pub async fn process_pending(&mut self) {
// Placeholder for batch notification processing
// Could be used for email queue processing, etc.
}
/// Load status from disk
fn load_status(file_path: &str) -> (HashMap<String, Status>, HashMap<String, String>) {

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use std::time::Instant;
use tracing::{debug, info, error};
use serde::{Deserialize, Serialize};
use crate::notifications::{NotificationManager, StatusChange};
use crate::notifications::StatusChange;
use chrono::Utc;
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -25,14 +25,6 @@ impl Default for HostStatusConfig {
}
}
pub struct StatusChangeEvent {
pub service_name: String,
pub old_status: Status,
pub new_status: Status,
pub host_status_changed: bool,
pub old_host_status: Status,
pub new_host_status: Status,
}
#[derive(Debug, Clone)]
pub struct StatusChangeSummary {
@ -59,7 +51,6 @@ pub struct HostStatusManager {
previous_host_status: Status,
last_status_change: Option<Instant>,
config: HostStatusConfig,
notification_manager: Option<NotificationManager>,
// Notification batching
pending_changes: HashMap<String, (Status, Status, usize)>, // service -> (initial_status, current_status, change_count)
batch_start_time: Option<Instant>,
@ -75,7 +66,6 @@ impl HostStatusManager {
previous_host_status: Status::Unknown,
last_status_change: None,
config,
notification_manager: None,
pending_changes: HashMap::new(),
batch_start_time: None,
batch_start_host_status: Status::Unknown,
@ -157,74 +147,7 @@ impl HostStatusManager {
}
}
/// Determine whether a notification should be sent for this status change
/// This implements the core logic: suppress individual recoveries, only notify on full host recovery
pub fn should_send_notification(&self, event: &StatusChangeEvent) -> bool {
if !self.config.enabled {
return false;
}
match (event.old_status, event.new_status) {
// Always notify on service problems (transitions TO warning/critical)
(_, Status::Warning) | (_, Status::Critical) => {
debug!("Notification approved: service '{}' transitioned to {:?}", event.service_name, event.new_status);
true
},
// Only notify on recovery if ALL services are OK (host status is OK)
(Status::Warning | Status::Critical | Status::Unknown, Status::Ok) => {
let should_notify = self.current_host_status == Status::Ok;
if should_notify {
info!("Recovery notification approved: service '{}' recovered and all services are OK", event.service_name);
} else {
debug!("Recovery notification suppressed: service '{}' recovered but other services still have issues (host status: {:?})",
event.service_name, self.current_host_status);
}
should_notify
},
// No notification for other transitions
_ => {
debug!("No notification needed for status transition: {:?} -> {:?}", event.old_status, event.new_status);
false
}
}
}
/// Get the current overall host status
pub fn get_host_status(&self) -> Status {
self.current_host_status
}
/// Check if all services are in OK status
pub fn all_services_ok(&self) -> bool {
!self.service_statuses.is_empty() &&
self.service_statuses.values().all(|s| *s == Status::Ok)
}
/// Get the current service statuses (for debugging/monitoring)
pub fn get_service_statuses(&self) -> &HashMap<String, Status> {
&self.service_statuses
}
/// Get the number of services in each status
pub fn get_status_summary(&self) -> (usize, usize, usize, usize, usize) {
let mut ok_count = 0;
let mut pending_count = 0;
let mut warning_count = 0;
let mut critical_count = 0;
let mut unknown_count = 0;
for status in self.service_statuses.values() {
match status {
Status::Ok => ok_count += 1,
Status::Pending => pending_count += 1,
Status::Warning => warning_count += 1,
Status::Critical => critical_count += 1,
Status::Unknown => unknown_count += 1,
}
}
(ok_count, pending_count, warning_count, critical_count, unknown_count)
}
/// Process a metric - updates status (notifications handled separately via batching)
pub async fn process_metric(&mut self, metric: &Metric, _notification_manager: &mut crate::notifications::NotificationManager) {
@ -345,10 +268,10 @@ impl HostStatusManager {
fn format_aggregated_details(&self, aggregated: &AggregatedStatusChanges) -> String {
let mut details = String::new();
let duration = aggregated.end_time.duration_since(aggregated.start_time).as_secs();
details.push_str(&format!(
"Status Summary ({} to {})\n",
aggregated.start_time.elapsed().as_secs(),
0
"Status Summary ({}s duration)\n",
duration
));
if aggregated.host_status_initial != aggregated.host_status_final {

View File

@ -143,6 +143,7 @@ impl Theme {
pub fn status_color(status: Status) -> Color {
match status {
Status::Ok => Self::success(),
Status::Pending => Self::info(), // Blue for pending
Status::Warning => Self::warning(),
Status::Critical => Self::error(),
Status::Unknown => Self::muted_text(),
@ -245,6 +246,7 @@ impl StatusIcons {
pub fn get_icon(status: Status) -> &'static str {
match status {
Status::Ok => "",
Status::Pending => "", // Hollow circle for pending
Status::Warning => "",
Status::Critical => "",
Status::Unknown => "?",
@ -256,6 +258,7 @@ impl StatusIcons {
let icon = Self::get_icon(status);
let status_color = match status {
Status::Ok => Theme::success(), // Green
Status::Pending => Theme::info(), // Blue
Status::Warning => Theme::warning(), // Yellow
Status::Critical => Theme::error(), // Red
Status::Unknown => Theme::muted_text(), // Gray

View File

@ -382,6 +382,7 @@ impl BackupWidget {
"Status: {}",
match self.overall_status {
Status::Ok => "OK",
Status::Pending => "Pending",
Status::Warning => "Warning",
Status::Critical => "Failed",
Status::Unknown => "Unknown",

View File

@ -112,6 +112,7 @@ impl ServicesWidget {
// Parent services always show active/inactive status
let status_str = match info.widget_status {
Status::Ok => "active".to_string(),
Status::Pending => "pending".to_string(),
Status::Warning => "inactive".to_string(),
Status::Critical => "failed".to_string(),
Status::Unknown => "unknown".to_string(),
@ -146,6 +147,7 @@ impl ServicesWidget {
} else {
match info.widget_status {
Status::Ok => "active".to_string(),
Status::Pending => "pending".to_string(),
Status::Warning => "inactive".to_string(),
Status::Critical => "failed".to_string(),
Status::Unknown => "unknown".to_string(),
@ -154,6 +156,7 @@ impl ServicesWidget {
let status_color = match info.widget_status {
Status::Ok => Theme::success(),
Status::Pending => Theme::info(),
Status::Warning => Theme::warning(),
Status::Critical => Theme::error(),
Status::Unknown => Theme::muted_text(),