Testing
This commit is contained in:
@@ -259,7 +259,7 @@ impl App {
|
||||
if service_metrics.timestamp != timestamp {
|
||||
service_metrics.timestamp = timestamp;
|
||||
}
|
||||
let mut snapshot = service_metrics.clone();
|
||||
let snapshot = service_metrics.clone();
|
||||
|
||||
// No more need for dashboard-side description caching since agent handles it
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ pub struct ServiceSummary {
|
||||
#[serde(default)]
|
||||
pub cpu_temp_c: Option<f32>,
|
||||
#[serde(default)]
|
||||
pub cpu_temp_status: Option<String>,
|
||||
#[serde(default)]
|
||||
pub gpu_load_percent: Option<f32>,
|
||||
#[serde(default)]
|
||||
pub gpu_temp_c: Option<f32>,
|
||||
@@ -100,7 +102,7 @@ pub enum ServiceStatus {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BackupMetrics {
|
||||
pub overall_status: BackupStatus,
|
||||
pub overall_status: String,
|
||||
pub backup: BackupInfo,
|
||||
pub service: BackupServiceInfo,
|
||||
#[serde(default)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use ratatui::layout::{Constraint, Rect};
|
||||
use ratatui::style::Color;
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::HostDisplayData;
|
||||
@@ -8,17 +7,7 @@ use crate::ui::system::{evaluate_performance, PerfSeverity};
|
||||
use crate::ui::widget::{render_widget_data, WidgetData, WidgetStatus, StatusLevel};
|
||||
|
||||
pub fn render(frame: &mut Frame, hosts: &[HostDisplayData], area: Rect) {
|
||||
let (severity, ok_count, warn_count, fail_count) = classify_hosts(hosts);
|
||||
let mut color = match severity {
|
||||
AlertSeverity::Critical => Color::Red,
|
||||
AlertSeverity::Warning => Color::Yellow,
|
||||
AlertSeverity::Healthy => Color::Green,
|
||||
AlertSeverity::Unknown => Color::Gray,
|
||||
};
|
||||
|
||||
if hosts.is_empty() {
|
||||
color = Color::Gray;
|
||||
}
|
||||
let (severity, _ok_count, _warn_count, _fail_count) = classify_hosts(hosts);
|
||||
|
||||
let title = "Alerts".to_string();
|
||||
|
||||
@@ -140,9 +129,9 @@ fn host_severity(host: &HostDisplayData) -> AlertSeverity {
|
||||
}
|
||||
|
||||
if let Some(backup) = host.backup.as_ref() {
|
||||
match backup.overall_status {
|
||||
crate::data::metrics::BackupStatus::Failed => return AlertSeverity::Critical,
|
||||
crate::data::metrics::BackupStatus::Warning => return AlertSeverity::Warning,
|
||||
match backup.overall_status.as_str() {
|
||||
"critical" => return AlertSeverity::Critical,
|
||||
"warning" => return AlertSeverity::Warning,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -211,15 +200,15 @@ fn host_status(host: &HostDisplayData) -> (String, AlertSeverity, bool) {
|
||||
}
|
||||
|
||||
if let Some(backup) = host.backup.as_ref() {
|
||||
match backup.overall_status {
|
||||
crate::data::metrics::BackupStatus::Failed => {
|
||||
match backup.overall_status.as_str() {
|
||||
"critical" => {
|
||||
return (
|
||||
"critical: backup failed".to_string(),
|
||||
AlertSeverity::Critical,
|
||||
true,
|
||||
);
|
||||
}
|
||||
crate::data::metrics::BackupStatus::Warning => {
|
||||
"warning" => {
|
||||
return (
|
||||
"warning: backup warning".to_string(),
|
||||
AlertSeverity::Warning,
|
||||
@@ -243,14 +232,6 @@ fn host_status(host: &HostDisplayData) -> (String, AlertSeverity, bool) {
|
||||
("ok".to_string(), AlertSeverity::Healthy, false)
|
||||
}
|
||||
|
||||
fn severity_color(severity: AlertSeverity) -> Color {
|
||||
match severity {
|
||||
AlertSeverity::Critical => Color::Red,
|
||||
AlertSeverity::Warning => Color::Yellow,
|
||||
AlertSeverity::Healthy => Color::Green,
|
||||
AlertSeverity::Unknown => Color::Gray,
|
||||
}
|
||||
}
|
||||
|
||||
fn latest_timestamp(host: &HostDisplayData) -> Option<DateTime<Utc>> {
|
||||
let mut latest = host.last_success;
|
||||
@@ -279,11 +260,3 @@ fn latest_timestamp(host: &HostDisplayData) -> Option<DateTime<Utc>> {
|
||||
latest
|
||||
}
|
||||
|
||||
fn severity_symbol(severity: AlertSeverity) -> &'static str {
|
||||
match severity {
|
||||
AlertSeverity::Critical => "✖",
|
||||
AlertSeverity::Warning => "!",
|
||||
AlertSeverity::Healthy => "✔",
|
||||
AlertSeverity::Unknown => "?",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::style::Color;
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::HostDisplayData;
|
||||
use crate::data::metrics::{BackupMetrics, BackupStatus};
|
||||
use crate::ui::widget::{render_placeholder, render_widget_data, WidgetData, WidgetStatus, StatusLevel};
|
||||
use crate::data::metrics::BackupMetrics;
|
||||
use crate::ui::widget::{render_placeholder, render_widget_data, status_level_from_agent_status, WidgetData, WidgetStatus, StatusLevel};
|
||||
|
||||
pub fn render(frame: &mut Frame, host: Option<&HostDisplayData>, area: Rect) {
|
||||
match host {
|
||||
@@ -25,12 +24,7 @@ pub fn render(frame: &mut Frame, host: Option<&HostDisplayData>, area: Rect) {
|
||||
}
|
||||
|
||||
fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &BackupMetrics, area: Rect) {
|
||||
let widget_status = match metrics.overall_status {
|
||||
BackupStatus::Failed => StatusLevel::Error,
|
||||
BackupStatus::Warning => StatusLevel::Warning,
|
||||
BackupStatus::Unknown => StatusLevel::Unknown,
|
||||
BackupStatus::Healthy => StatusLevel::Ok,
|
||||
};
|
||||
let widget_status = status_level_from_agent_status(Some(&metrics.overall_status));
|
||||
|
||||
let mut data = WidgetData::new(
|
||||
"Backups",
|
||||
@@ -93,46 +87,4 @@ fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &BackupMe
|
||||
render_widget_data(frame, area, data);
|
||||
}
|
||||
|
||||
fn backup_status_color(status: &BackupStatus) -> Color {
|
||||
match status {
|
||||
BackupStatus::Failed => Color::Red,
|
||||
BackupStatus::Warning => Color::Yellow,
|
||||
BackupStatus::Unknown => Color::LightYellow,
|
||||
BackupStatus::Healthy => Color::Green,
|
||||
}
|
||||
}
|
||||
|
||||
fn format_timestamp(timestamp: Option<&chrono::DateTime<chrono::Utc>>) -> String {
|
||||
timestamp
|
||||
.map(|ts| ts.format("%Y-%m-%d %H:%M:%S").to_string())
|
||||
.unwrap_or_else(|| "—".to_string())
|
||||
}
|
||||
|
||||
fn repo_status_level(metrics: &BackupMetrics) -> StatusLevel {
|
||||
match metrics.overall_status {
|
||||
BackupStatus::Failed => StatusLevel::Error,
|
||||
BackupStatus::Warning => StatusLevel::Warning,
|
||||
_ => {
|
||||
if metrics.backup.snapshot_count > 0 {
|
||||
StatusLevel::Ok
|
||||
} else {
|
||||
StatusLevel::Warning
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn service_status_level(metrics: &BackupMetrics) -> StatusLevel {
|
||||
match metrics.overall_status {
|
||||
BackupStatus::Failed => StatusLevel::Error,
|
||||
BackupStatus::Warning => StatusLevel::Warning,
|
||||
BackupStatus::Unknown => StatusLevel::Unknown,
|
||||
BackupStatus::Healthy => {
|
||||
if metrics.service.enabled {
|
||||
StatusLevel::Ok
|
||||
} else {
|
||||
StatusLevel::Warning
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::style::Color;
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::HostDisplayData;
|
||||
use crate::data::metrics::{ServiceStatus, ServiceSummary};
|
||||
use crate::data::metrics::ServiceStatus;
|
||||
use crate::ui::widget::{render_placeholder, render_widget_data, status_level_from_agent_status, WidgetData, WidgetStatus, StatusLevel};
|
||||
|
||||
pub fn render(frame: &mut Frame, host: Option<&HostDisplayData>, area: Rect) {
|
||||
@@ -31,7 +30,6 @@ fn render_metrics(
|
||||
area: Rect,
|
||||
) {
|
||||
let summary = &metrics.summary;
|
||||
let color = summary_color(summary);
|
||||
let title = "Services".to_string();
|
||||
|
||||
// Use agent-calculated services status
|
||||
@@ -105,24 +103,6 @@ fn status_weight(status: &ServiceStatus) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
fn status_symbol(status: &ServiceStatus) -> (&'static str, Color) {
|
||||
match status {
|
||||
ServiceStatus::Running => ("✔", Color::Green),
|
||||
ServiceStatus::Degraded => ("!", Color::Yellow),
|
||||
ServiceStatus::Restarting => ("↻", Color::Yellow),
|
||||
ServiceStatus::Stopped => ("✖", Color::Red),
|
||||
}
|
||||
}
|
||||
|
||||
fn summary_color(summary: &ServiceSummary) -> Color {
|
||||
if summary.failed > 0 {
|
||||
Color::Red
|
||||
} else if summary.degraded > 0 {
|
||||
Color::Yellow
|
||||
} else {
|
||||
Color::Green
|
||||
}
|
||||
}
|
||||
|
||||
fn format_memory_value(used: f32, quota: f32) -> String {
|
||||
let used_gb = used / 1000.0;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::style::Color;
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::HostDisplayData;
|
||||
use crate::data::metrics::SmartMetrics;
|
||||
use crate::ui::widget::{render_placeholder, render_widget_data, WidgetData, WidgetStatus, StatusLevel};
|
||||
use crate::ui::widget::{render_placeholder, render_widget_data, status_level_from_agent_status, WidgetData, WidgetStatus, StatusLevel};
|
||||
|
||||
pub fn render(frame: &mut Frame, host: Option<&HostDisplayData>, area: Rect) {
|
||||
match host {
|
||||
@@ -25,16 +24,9 @@ pub fn render(frame: &mut Frame, host: Option<&HostDisplayData>, area: Rect) {
|
||||
}
|
||||
|
||||
fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &SmartMetrics, area: Rect) {
|
||||
let color = smart_status_color(&metrics.status);
|
||||
let title = "Storage".to_string();
|
||||
|
||||
let widget_status = if metrics.summary.critical > 0 {
|
||||
StatusLevel::Error
|
||||
} else if metrics.summary.warning > 0 {
|
||||
StatusLevel::Warning
|
||||
} else {
|
||||
StatusLevel::Ok
|
||||
};
|
||||
let widget_status = status_level_from_agent_status(Some(&metrics.status));
|
||||
|
||||
let mut data = WidgetData::new(
|
||||
title,
|
||||
@@ -95,13 +87,6 @@ fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &SmartMet
|
||||
render_widget_data(frame, area, data);
|
||||
}
|
||||
|
||||
fn smart_status_color(status: &str) -> Color {
|
||||
match status.to_uppercase().as_str() {
|
||||
"CRITICAL" => Color::Red,
|
||||
"WARNING" => Color::Yellow,
|
||||
_ => Color::Green,
|
||||
}
|
||||
}
|
||||
|
||||
fn format_temperature(value: f32) -> String {
|
||||
if value.abs() < f32::EPSILON {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::style::Color;
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::HostDisplayData;
|
||||
use crate::data::metrics::{ServiceMetrics, ServiceSummary};
|
||||
use crate::ui::widget::{
|
||||
combined_color, render_placeholder, render_combined_widget_data, status_color_for_cpu_load, status_color_from_metric,
|
||||
status_color_from_percentage, status_level_from_agent_status, WidgetDataSet, WidgetStatus, StatusLevel,
|
||||
render_placeholder, render_combined_widget_data,
|
||||
status_level_from_agent_status, WidgetDataSet, WidgetStatus, StatusLevel,
|
||||
};
|
||||
|
||||
pub fn render(frame: &mut Frame, host: Option<&HostDisplayData>, area: Rect) {
|
||||
@@ -44,33 +43,19 @@ fn render_metrics(
|
||||
} else {
|
||||
summary.memory_used_mb
|
||||
};
|
||||
let usage_ratio = if system_total > 0.0 {
|
||||
let _usage_ratio = if system_total > 0.0 {
|
||||
(system_used / system_total) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let (perf_severity, _reason) = evaluate_performance(summary);
|
||||
let border_color = match perf_severity {
|
||||
PerfSeverity::Critical => Color::Red,
|
||||
PerfSeverity::Warning => Color::Yellow,
|
||||
PerfSeverity::Ok => Color::Green,
|
||||
};
|
||||
// Dashboard should NOT calculate border colors - agent is the source of truth
|
||||
|
||||
// Use agent-calculated statuses instead of dashboard calculations
|
||||
let memory_status = status_level_from_agent_status(summary.memory_status.as_ref());
|
||||
let cpu_status = status_level_from_agent_status(summary.cpu_status.as_ref());
|
||||
let cpu_temp_color = status_color_from_metric(summary.cpu_temp_c, 80.0, 90.0);
|
||||
let gpu_load_color = summary
|
||||
.gpu_load_percent
|
||||
.map(|value| status_color_from_percentage(value, 85.0, 95.0))
|
||||
.unwrap_or(Color::Green);
|
||||
let gpu_temp_color = summary
|
||||
.gpu_temp_c
|
||||
.map(|value| status_color_from_metric(Some(value), 75.0, 85.0))
|
||||
.unwrap_or(Color::Green);
|
||||
|
||||
let gpu_icon_color = combined_color(&[gpu_load_color, gpu_temp_color]);
|
||||
// Dashboard should NOT calculate colors - agent is the source of truth
|
||||
|
||||
// Memory dataset - use agent-calculated status
|
||||
let mut memory_dataset = WidgetDataSet::new(vec!["Memory usage".to_string()], Some(WidgetStatus::new(memory_status)));
|
||||
@@ -156,7 +141,8 @@ fn render_metrics(
|
||||
}
|
||||
|
||||
// GPU dataset
|
||||
let gpu_status = status_level_from_color(gpu_icon_color);
|
||||
// GPU status should come from agent when available
|
||||
let gpu_status = StatusLevel::Unknown; // Default until agent provides gpu_status
|
||||
let mut gpu_dataset = WidgetDataSet::new(vec!["GPU load".to_string(), "GPU temp".to_string()], Some(WidgetStatus::new(gpu_status)));
|
||||
gpu_dataset.add_row(
|
||||
Some(WidgetStatus::new(gpu_status)),
|
||||
@@ -206,13 +192,6 @@ fn format_optional_percent(value: Option<f32>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn status_level_from_color(color: Color) -> StatusLevel {
|
||||
match color {
|
||||
Color::Red => StatusLevel::Error,
|
||||
Color::Yellow => StatusLevel::Warning,
|
||||
_ => StatusLevel::Ok,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn evaluate_performance(summary: &ServiceSummary) -> (PerfSeverity, Option<String>) {
|
||||
let mem_percent = if summary.system_memory_total_mb > 0.0 {
|
||||
@@ -233,43 +212,38 @@ pub(crate) fn evaluate_performance(summary: &ServiceSummary) -> (PerfSeverity, O
|
||||
}
|
||||
};
|
||||
|
||||
if mem_percent >= 95.0 {
|
||||
consider(PerfSeverity::Critical, format!("RAM {:.0}%", mem_percent));
|
||||
} else if mem_percent >= 80.0 {
|
||||
consider(PerfSeverity::Warning, format!("RAM {:.0}%", mem_percent));
|
||||
}
|
||||
|
||||
let load = summary.cpu_load_5;
|
||||
if load >= 4.0 {
|
||||
consider(PerfSeverity::Critical, format!("CPU load {:.2}", load));
|
||||
} else if load >= 2.0 {
|
||||
consider(PerfSeverity::Warning, format!("CPU load {:.2}", load));
|
||||
}
|
||||
|
||||
if let Some(temp) = summary.cpu_temp_c {
|
||||
if temp >= 90.0 {
|
||||
consider(PerfSeverity::Critical, format!("CPU temp {:.0}°C", temp));
|
||||
} else if temp >= 80.0 {
|
||||
consider(PerfSeverity::Warning, format!("CPU temp {:.0}°C", temp));
|
||||
// Use agent's memory status instead of hardcoded thresholds
|
||||
if let Some(memory_status) = &summary.memory_status {
|
||||
match memory_status.as_str() {
|
||||
"critical" => consider(PerfSeverity::Critical, format!("RAM {:.0}%", mem_percent)),
|
||||
"warning" => consider(PerfSeverity::Warning, format!("RAM {:.0}%", mem_percent)),
|
||||
_ => {} // "ok" - no alert needed
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(load) = summary.gpu_load_percent {
|
||||
if load >= 95.0 {
|
||||
consider(PerfSeverity::Critical, format!("GPU load {:.0}%", load));
|
||||
} else if load >= 85.0 {
|
||||
consider(PerfSeverity::Warning, format!("GPU load {:.0}%", load));
|
||||
// Use agent's CPU status instead of hardcoded thresholds
|
||||
if let Some(cpu_status) = &summary.cpu_status {
|
||||
match cpu_status.as_str() {
|
||||
"critical" => consider(PerfSeverity::Critical, format!("CPU load {:.2}", summary.cpu_load_5)),
|
||||
"warning" => consider(PerfSeverity::Warning, format!("CPU load {:.2}", summary.cpu_load_5)),
|
||||
_ => {} // "ok" - no alert needed
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(temp) = summary.gpu_temp_c {
|
||||
if temp >= 85.0 {
|
||||
consider(PerfSeverity::Critical, format!("GPU temp {:.0}°C", temp));
|
||||
} else if temp >= 75.0 {
|
||||
consider(PerfSeverity::Warning, format!("GPU temp {:.0}°C", temp));
|
||||
// Use agent's CPU temperature status instead of hardcoded thresholds
|
||||
if let Some(cpu_temp_status) = &summary.cpu_temp_status {
|
||||
if let Some(temp) = summary.cpu_temp_c {
|
||||
match cpu_temp_status.as_str() {
|
||||
"critical" => consider(PerfSeverity::Critical, format!("CPU temp {:.0}°C", temp)),
|
||||
"warning" => consider(PerfSeverity::Warning, format!("CPU temp {:.0}°C", temp)),
|
||||
_ => {} // "ok" - no alert needed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: GPU status should come from agent, not calculated here with hardcoded thresholds
|
||||
// For now, remove hardcoded GPU thresholds until agent provides gpu_status
|
||||
|
||||
if severity == PerfSeverity::Ok {
|
||||
(PerfSeverity::Ok, None)
|
||||
} else {
|
||||
|
||||
@@ -24,33 +24,8 @@ fn neutral_border_style(color: Color) -> Style {
|
||||
Style::default().fg(color)
|
||||
}
|
||||
|
||||
pub fn status_color_from_percentage(value: f32, warn: f32, crit: f32) -> Color {
|
||||
if value >= crit {
|
||||
Color::Red
|
||||
} else if value >= warn {
|
||||
Color::Yellow
|
||||
} else {
|
||||
Color::Green
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status_color_from_metric(value: Option<f32>, warn: f32, crit: f32) -> Color {
|
||||
match value {
|
||||
Some(v) if v >= crit => Color::Red,
|
||||
Some(v) if v >= warn => Color::Yellow,
|
||||
_ => Color::Green,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status_color_for_cpu_load(load: f32) -> Color {
|
||||
if load >= 8.0 {
|
||||
Color::Red
|
||||
} else if load >= 5.0 {
|
||||
Color::Yellow
|
||||
} else {
|
||||
Color::Green
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status_level_from_agent_status(agent_status: Option<&String>) -> StatusLevel {
|
||||
match agent_status.map(|s| s.as_str()) {
|
||||
@@ -62,15 +37,6 @@ pub fn status_level_from_agent_status(agent_status: Option<&String>) -> StatusLe
|
||||
}
|
||||
}
|
||||
|
||||
pub fn combined_color(colors: &[Color]) -> Color {
|
||||
if colors.iter().any(|&c| c == Color::Red) {
|
||||
Color::Red
|
||||
} else if colors.iter().any(|&c| c == Color::Yellow) {
|
||||
Color::Yellow
|
||||
} else {
|
||||
Color::Green
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn render_placeholder(frame: &mut Frame, area: Rect, title: &str, message: &str) {
|
||||
|
||||
Reference in New Issue
Block a user