|
|
|
|
@@ -6,6 +6,7 @@ use ratatui::{
|
|
|
|
|
widgets::{Block, Paragraph},
|
|
|
|
|
Frame,
|
|
|
|
|
};
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use std::time::Instant;
|
|
|
|
|
use tracing::info;
|
|
|
|
|
|
|
|
|
|
@@ -17,24 +18,43 @@ use crate::metrics::{MetricStore, WidgetType};
|
|
|
|
|
use cm_dashboard_shared::{Metric, Status};
|
|
|
|
|
use theme::{Theme, Layout as ThemeLayout, Typography, Components, StatusIcons};
|
|
|
|
|
|
|
|
|
|
/// Widget states for a specific host
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct HostWidgets {
|
|
|
|
|
/// CPU widget state
|
|
|
|
|
pub cpu_widget: CpuWidget,
|
|
|
|
|
/// Memory widget state
|
|
|
|
|
pub memory_widget: MemoryWidget,
|
|
|
|
|
/// Services widget state
|
|
|
|
|
pub services_widget: ServicesWidget,
|
|
|
|
|
/// Backup widget state
|
|
|
|
|
pub backup_widget: BackupWidget,
|
|
|
|
|
/// Last update time for this host
|
|
|
|
|
pub last_update: Option<Instant>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl HostWidgets {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
cpu_widget: CpuWidget::new(),
|
|
|
|
|
memory_widget: MemoryWidget::new(),
|
|
|
|
|
services_widget: ServicesWidget::new(),
|
|
|
|
|
backup_widget: BackupWidget::new(),
|
|
|
|
|
last_update: None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Main TUI application
|
|
|
|
|
pub struct TuiApp {
|
|
|
|
|
/// CPU widget
|
|
|
|
|
cpu_widget: CpuWidget,
|
|
|
|
|
/// Memory widget
|
|
|
|
|
memory_widget: MemoryWidget,
|
|
|
|
|
/// Services widget
|
|
|
|
|
services_widget: ServicesWidget,
|
|
|
|
|
/// Backup widget
|
|
|
|
|
backup_widget: BackupWidget,
|
|
|
|
|
/// Widget states per host (hostname -> HostWidgets)
|
|
|
|
|
host_widgets: HashMap<String, HostWidgets>,
|
|
|
|
|
/// Current active host
|
|
|
|
|
current_host: Option<String>,
|
|
|
|
|
/// Available hosts
|
|
|
|
|
available_hosts: Vec<String>,
|
|
|
|
|
/// Host index for navigation
|
|
|
|
|
host_index: usize,
|
|
|
|
|
/// Last update time
|
|
|
|
|
last_update: Option<Instant>,
|
|
|
|
|
/// Should quit application
|
|
|
|
|
should_quit: bool,
|
|
|
|
|
}
|
|
|
|
|
@@ -42,45 +62,44 @@ pub struct TuiApp {
|
|
|
|
|
impl TuiApp {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
cpu_widget: CpuWidget::new(),
|
|
|
|
|
memory_widget: MemoryWidget::new(),
|
|
|
|
|
services_widget: ServicesWidget::new(),
|
|
|
|
|
backup_widget: BackupWidget::new(),
|
|
|
|
|
host_widgets: HashMap::new(),
|
|
|
|
|
current_host: None,
|
|
|
|
|
available_hosts: Vec::new(),
|
|
|
|
|
host_index: 0,
|
|
|
|
|
last_update: None,
|
|
|
|
|
should_quit: false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get or create host widgets for the given hostname
|
|
|
|
|
fn get_or_create_host_widgets(&mut self, hostname: &str) -> &mut HostWidgets {
|
|
|
|
|
self.host_widgets.entry(hostname.to_string()).or_insert_with(HostWidgets::new)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Update widgets with metrics from store
|
|
|
|
|
pub fn update_metrics(&mut self, metric_store: &MetricStore) {
|
|
|
|
|
if let Some(ref hostname) = self.current_host {
|
|
|
|
|
// Update CPU widget
|
|
|
|
|
let cpu_metrics = metric_store.get_metrics_for_widget(hostname, WidgetType::Cpu);
|
|
|
|
|
self.cpu_widget.update_from_metrics(&cpu_metrics);
|
|
|
|
|
|
|
|
|
|
// Update Memory widget
|
|
|
|
|
let memory_metrics = metric_store.get_metrics_for_widget(hostname, WidgetType::Memory);
|
|
|
|
|
self.memory_widget.update_from_metrics(&memory_metrics);
|
|
|
|
|
|
|
|
|
|
// Update Services widget - get all metrics that start with "service_"
|
|
|
|
|
let all_metrics = metric_store.get_metrics_for_host(hostname);
|
|
|
|
|
if let Some(hostname) = self.current_host.clone() {
|
|
|
|
|
// Get metrics first while hostname is borrowed
|
|
|
|
|
let cpu_metrics = metric_store.get_metrics_for_widget(&hostname, WidgetType::Cpu);
|
|
|
|
|
let memory_metrics = metric_store.get_metrics_for_widget(&hostname, WidgetType::Memory);
|
|
|
|
|
let all_metrics = metric_store.get_metrics_for_host(&hostname);
|
|
|
|
|
let service_metrics: Vec<&Metric> = all_metrics.iter()
|
|
|
|
|
.filter(|m| m.name.starts_with("service_"))
|
|
|
|
|
.copied()
|
|
|
|
|
.collect();
|
|
|
|
|
self.services_widget.update_from_metrics(&service_metrics);
|
|
|
|
|
|
|
|
|
|
// Update Backup widget - get all metrics that start with "backup_"
|
|
|
|
|
let all_backup_metrics: Vec<&Metric> = all_metrics.iter()
|
|
|
|
|
.filter(|m| m.name.starts_with("backup_"))
|
|
|
|
|
.copied()
|
|
|
|
|
.collect();
|
|
|
|
|
self.backup_widget.update_from_metrics(&all_backup_metrics);
|
|
|
|
|
|
|
|
|
|
self.last_update = Some(Instant::now());
|
|
|
|
|
// Now get host widgets and update them
|
|
|
|
|
let host_widgets = self.get_or_create_host_widgets(&hostname);
|
|
|
|
|
|
|
|
|
|
host_widgets.cpu_widget.update_from_metrics(&cpu_metrics);
|
|
|
|
|
host_widgets.memory_widget.update_from_metrics(&memory_metrics);
|
|
|
|
|
host_widgets.services_widget.update_from_metrics(&service_metrics);
|
|
|
|
|
host_widgets.backup_widget.update_from_metrics(&all_backup_metrics);
|
|
|
|
|
|
|
|
|
|
host_widgets.last_update = Some(Instant::now());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -184,7 +203,12 @@ impl TuiApp {
|
|
|
|
|
// Render new panel layout
|
|
|
|
|
self.render_system_panel(frame, left_chunks[0], metric_store);
|
|
|
|
|
self.render_backup_panel(frame, left_chunks[1]);
|
|
|
|
|
self.services_widget.render(frame, content_chunks[1]); // Services takes full right side
|
|
|
|
|
|
|
|
|
|
// Render services widget for current host
|
|
|
|
|
if let Some(hostname) = self.current_host.clone() {
|
|
|
|
|
let host_widgets = self.get_or_create_host_widgets(&hostname);
|
|
|
|
|
host_widgets.services_widget.render(frame, content_chunks[1]); // Services takes full right side
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Render btop-style minimal title
|
|
|
|
|
@@ -241,8 +265,12 @@ impl TuiApp {
|
|
|
|
|
])
|
|
|
|
|
.split(inner_area);
|
|
|
|
|
|
|
|
|
|
self.cpu_widget.render(frame, content_chunks[0]);
|
|
|
|
|
self.memory_widget.render(frame, content_chunks[1]);
|
|
|
|
|
// Get current host widgets, create if none exist
|
|
|
|
|
if let Some(hostname) = self.current_host.clone() {
|
|
|
|
|
let host_widgets = self.get_or_create_host_widgets(&hostname);
|
|
|
|
|
host_widgets.cpu_widget.render(frame, content_chunks[0]);
|
|
|
|
|
host_widgets.memory_widget.render(frame, content_chunks[1]);
|
|
|
|
|
}
|
|
|
|
|
self.render_storage_section(frame, content_chunks[2], metric_store);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -250,7 +278,12 @@ impl TuiApp {
|
|
|
|
|
let backup_block = Components::widget_block("backup");
|
|
|
|
|
let inner_area = backup_block.inner(area);
|
|
|
|
|
frame.render_widget(backup_block, area);
|
|
|
|
|
self.backup_widget.render(frame, inner_area);
|
|
|
|
|
|
|
|
|
|
// Get current host widgets for backup widget
|
|
|
|
|
if let Some(hostname) = self.current_host.clone() {
|
|
|
|
|
let host_widgets = self.get_or_create_host_widgets(&hostname);
|
|
|
|
|
host_widgets.backup_widget.render(frame, inner_area);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|