Fix storage display format and clean up warnings
All checks were successful
Build and Release / build-and-release (push) Successful in 1m9s
All checks were successful
Build and Release / build-and-release (push) Successful in 1m9s
Update storage display to match CLAUDE.md specification: - Show drive temp/wear on main line: nvme0n1 T: 25°C W: 4% - Display individual filesystems as sub-items: /: 55% 250.5GB/456.4GB - Remove Total usage line in favor of filesystem breakdown Clean up code warnings: - Remove unused heartbeat methods and fields - Remove unused backup widget fields and methods - Add allow attributes for legacy methods
This commit is contained in:
parent
bea2d120b5
commit
11d1c2dc94
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -279,7 +279,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cm-dashboard"
|
name = "cm-dashboard"
|
||||||
version = "0.1.136"
|
version = "0.1.137"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -301,7 +301,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cm-dashboard-agent"
|
name = "cm-dashboard-agent"
|
||||||
version = "0.1.136"
|
version = "0.1.137"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -324,7 +324,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cm-dashboard-shared"
|
name = "cm-dashboard-shared"
|
||||||
version = "0.1.136"
|
version = "0.1.137"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard-agent"
|
name = "cm-dashboard-agent"
|
||||||
version = "0.1.137"
|
version = "0.1.138"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -262,47 +262,11 @@ impl Agent {
|
|||||||
agent_data.system.memory.swap_used_gb = value;
|
agent_data.system.memory.swap_used_gb = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Tmpfs metrics
|
// Tmpfs metrics - handle multiple auto-discovered tmpfs mounts
|
||||||
else if metric.name.starts_with("memory_tmp_") {
|
else if metric.name.starts_with("memory_tmpfs_") {
|
||||||
// For now, create a single /tmp tmpfs entry
|
if let Some((mount_point, metric_type)) = self.parse_tmpfs_metric_name(&metric.name) {
|
||||||
if metric.name == "memory_tmp_usage_percent" {
|
|
||||||
if let Some(value) = metric.value.as_f32() {
|
if let Some(value) = metric.value.as_f32() {
|
||||||
if let Some(tmpfs) = agent_data.system.memory.tmpfs.get_mut(0) {
|
self.update_tmpfs_data(&mut agent_data.system.memory.tmpfs, &mount_point, &metric_type, value);
|
||||||
tmpfs.usage_percent = value;
|
|
||||||
} else {
|
|
||||||
agent_data.system.memory.tmpfs.push(TmpfsData {
|
|
||||||
mount: "/tmp".to_string(),
|
|
||||||
usage_percent: value,
|
|
||||||
used_gb: 0.0,
|
|
||||||
total_gb: 0.0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if metric.name == "memory_tmp_used_gb" {
|
|
||||||
if let Some(value) = metric.value.as_f32() {
|
|
||||||
if let Some(tmpfs) = agent_data.system.memory.tmpfs.get_mut(0) {
|
|
||||||
tmpfs.used_gb = value;
|
|
||||||
} else {
|
|
||||||
agent_data.system.memory.tmpfs.push(TmpfsData {
|
|
||||||
mount: "/tmp".to_string(),
|
|
||||||
usage_percent: 0.0,
|
|
||||||
used_gb: value,
|
|
||||||
total_gb: 0.0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if metric.name == "memory_tmp_total_gb" {
|
|
||||||
if let Some(value) = metric.value.as_f32() {
|
|
||||||
if let Some(tmpfs) = agent_data.system.memory.tmpfs.get_mut(0) {
|
|
||||||
tmpfs.total_gb = value;
|
|
||||||
} else {
|
|
||||||
agent_data.system.memory.tmpfs.push(TmpfsData {
|
|
||||||
mount: "/tmp".to_string(),
|
|
||||||
usage_percent: 0.0,
|
|
||||||
used_gb: 0.0,
|
|
||||||
total_gb: value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -394,6 +358,63 @@ impl Agent {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse tmpfs metric name to extract mount point and metric type
|
||||||
|
/// Example: "memory_tmpfs_tmp_usage_percent" -> ("/tmp", "usage_percent")
|
||||||
|
fn parse_tmpfs_metric_name(&self, metric_name: &str) -> Option<(String, String)> {
|
||||||
|
if !metric_name.starts_with("memory_tmpfs_") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let remainder = &metric_name[13..]; // Remove "memory_tmpfs_" prefix
|
||||||
|
|
||||||
|
// Find the last underscore to separate metric type from mount point
|
||||||
|
if let Some(last_underscore) = remainder.rfind('_') {
|
||||||
|
let mount_safe = &remainder[..last_underscore];
|
||||||
|
let metric_type = &remainder[last_underscore + 1..];
|
||||||
|
|
||||||
|
// Convert safe mount name back to actual mount point
|
||||||
|
let mount_point = if mount_safe.is_empty() {
|
||||||
|
"/"
|
||||||
|
} else {
|
||||||
|
&format!("/{}", mount_safe.replace('_', "/"))
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((mount_point.to_string(), metric_type.to_string()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update tmpfs data in the tmpfs vector
|
||||||
|
fn update_tmpfs_data(&self, tmpfs_vec: &mut Vec<TmpfsData>, mount_point: &str, metric_type: &str, value: f32) {
|
||||||
|
// Find existing tmpfs entry
|
||||||
|
let existing_index = tmpfs_vec.iter()
|
||||||
|
.position(|tmpfs| tmpfs.mount == mount_point);
|
||||||
|
|
||||||
|
let tmpfs_index = if let Some(index) = existing_index {
|
||||||
|
index
|
||||||
|
} else {
|
||||||
|
// Create new entry
|
||||||
|
tmpfs_vec.push(TmpfsData {
|
||||||
|
mount: mount_point.to_string(),
|
||||||
|
usage_percent: 0.0,
|
||||||
|
used_gb: 0.0,
|
||||||
|
total_gb: 0.0,
|
||||||
|
});
|
||||||
|
tmpfs_vec.len() - 1
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update the tmpfs entry
|
||||||
|
if let Some(tmpfs) = tmpfs_vec.get_mut(tmpfs_index) {
|
||||||
|
match metric_type {
|
||||||
|
"usage_percent" => tmpfs.usage_percent = value,
|
||||||
|
"used_gb" => tmpfs.used_gb = value,
|
||||||
|
"total_gb" => tmpfs.total_gb = value,
|
||||||
|
_ => {} // Unknown metric type, ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Extract drive name from metric like "disk_nvme0n1_temperature"
|
/// Extract drive name from metric like "disk_nvme0n1_temperature"
|
||||||
fn extract_drive_name(&self, metric_name: &str) -> Option<String> {
|
fn extract_drive_name(&self, metric_name: &str) -> Option<String> {
|
||||||
if metric_name.starts_with("disk_") {
|
if metric_name.starts_with("disk_") {
|
||||||
@ -529,31 +550,6 @@ impl Agent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create heartbeat metric for host connectivity detection
|
/// Create heartbeat metric for host connectivity detection
|
||||||
fn get_heartbeat_metric(&self) -> Metric {
|
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
|
|
||||||
let timestamp = SystemTime::now()
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_secs();
|
|
||||||
|
|
||||||
Metric::new(
|
|
||||||
"agent_heartbeat".to_string(),
|
|
||||||
MetricValue::Integer(timestamp as i64),
|
|
||||||
Status::Ok,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send standalone heartbeat for connectivity detection
|
|
||||||
async fn send_heartbeat(&mut self) -> Result<()> {
|
|
||||||
// Create minimal agent data with just heartbeat
|
|
||||||
let agent_data = AgentData::new(self.hostname.clone(), self.get_agent_version());
|
|
||||||
// Heartbeat timestamp is already set in AgentData::new()
|
|
||||||
|
|
||||||
self.zmq_handler.publish_agent_data(&agent_data).await?;
|
|
||||||
debug!("Sent standalone heartbeat for connectivity detection");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_commands(&mut self) -> Result<()> {
|
async fn handle_commands(&mut self) -> Result<()> {
|
||||||
// Try to receive commands (non-blocking)
|
// Try to receive commands (non-blocking)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard"
|
name = "cm-dashboard"
|
||||||
version = "0.1.137"
|
version = "0.1.138"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -18,8 +18,6 @@ pub struct BackupWidget {
|
|||||||
duration_seconds: Option<i64>,
|
duration_seconds: Option<i64>,
|
||||||
/// Last backup timestamp
|
/// Last backup timestamp
|
||||||
last_run_timestamp: Option<i64>,
|
last_run_timestamp: Option<i64>,
|
||||||
/// Total number of backup services
|
|
||||||
total_services: Option<i64>,
|
|
||||||
/// Total repository size in GB
|
/// Total repository size in GB
|
||||||
total_repo_size_gb: Option<f32>,
|
total_repo_size_gb: Option<f32>,
|
||||||
/// Total disk space for backups in GB
|
/// Total disk space for backups in GB
|
||||||
@ -32,14 +30,6 @@ pub struct BackupWidget {
|
|||||||
backup_disk_serial_number: Option<String>,
|
backup_disk_serial_number: Option<String>,
|
||||||
/// Backup disk wear percentage from SMART data
|
/// Backup disk wear percentage from SMART data
|
||||||
backup_disk_wear_percent: Option<f32>,
|
backup_disk_wear_percent: Option<f32>,
|
||||||
/// Backup disk filesystem label
|
|
||||||
backup_disk_filesystem_label: Option<String>,
|
|
||||||
/// Number of completed services
|
|
||||||
services_completed_count: Option<i64>,
|
|
||||||
/// Number of failed services
|
|
||||||
services_failed_count: Option<i64>,
|
|
||||||
/// Number of disabled services
|
|
||||||
services_disabled_count: Option<i64>,
|
|
||||||
/// All individual service metrics for detailed display
|
/// All individual service metrics for detailed display
|
||||||
service_metrics: Vec<ServiceMetricData>,
|
service_metrics: Vec<ServiceMetricData>,
|
||||||
/// Last update indicator
|
/// Last update indicator
|
||||||
@ -50,7 +40,6 @@ pub struct BackupWidget {
|
|||||||
struct ServiceMetricData {
|
struct ServiceMetricData {
|
||||||
name: String,
|
name: String,
|
||||||
status: Status,
|
status: Status,
|
||||||
exit_code: Option<i64>,
|
|
||||||
archive_count: Option<i64>,
|
archive_count: Option<i64>,
|
||||||
repo_size_gb: Option<f32>,
|
repo_size_gb: Option<f32>,
|
||||||
}
|
}
|
||||||
@ -61,17 +50,12 @@ impl BackupWidget {
|
|||||||
overall_status: Status::Unknown,
|
overall_status: Status::Unknown,
|
||||||
duration_seconds: None,
|
duration_seconds: None,
|
||||||
last_run_timestamp: None,
|
last_run_timestamp: None,
|
||||||
total_services: None,
|
|
||||||
total_repo_size_gb: None,
|
total_repo_size_gb: None,
|
||||||
backup_disk_total_gb: None,
|
backup_disk_total_gb: None,
|
||||||
backup_disk_used_gb: None,
|
backup_disk_used_gb: None,
|
||||||
backup_disk_product_name: None,
|
backup_disk_product_name: None,
|
||||||
backup_disk_serial_number: None,
|
backup_disk_serial_number: None,
|
||||||
backup_disk_wear_percent: None,
|
backup_disk_wear_percent: None,
|
||||||
backup_disk_filesystem_label: None,
|
|
||||||
services_completed_count: None,
|
|
||||||
services_failed_count: None,
|
|
||||||
services_disabled_count: None,
|
|
||||||
service_metrics: Vec::new(),
|
service_metrics: Vec::new(),
|
||||||
has_data: false,
|
has_data: false,
|
||||||
}
|
}
|
||||||
@ -112,6 +96,7 @@ impl BackupWidget {
|
|||||||
|
|
||||||
|
|
||||||
/// Extract service name from metric name (e.g., "backup_service_gitea_status" -> "gitea")
|
/// Extract service name from metric name (e.g., "backup_service_gitea_status" -> "gitea")
|
||||||
|
#[allow(dead_code)]
|
||||||
fn extract_service_name(metric_name: &str) -> Option<String> {
|
fn extract_service_name(metric_name: &str) -> Option<String> {
|
||||||
if metric_name.starts_with("backup_service_") {
|
if metric_name.starts_with("backup_service_") {
|
||||||
let name_part = &metric_name[15..]; // Remove "backup_service_" prefix
|
let name_part = &metric_name[15..]; // Remove "backup_service_" prefix
|
||||||
@ -119,8 +104,6 @@ impl BackupWidget {
|
|||||||
// Try to extract service name by removing known suffixes
|
// Try to extract service name by removing known suffixes
|
||||||
if let Some(service_name) = name_part.strip_suffix("_status") {
|
if let Some(service_name) = name_part.strip_suffix("_status") {
|
||||||
Some(service_name.to_string())
|
Some(service_name.to_string())
|
||||||
} else if let Some(service_name) = name_part.strip_suffix("_exit_code") {
|
|
||||||
Some(service_name.to_string())
|
|
||||||
} else if let Some(service_name) = name_part.strip_suffix("_archive_count") {
|
} else if let Some(service_name) = name_part.strip_suffix("_archive_count") {
|
||||||
Some(service_name.to_string())
|
Some(service_name.to_string())
|
||||||
} else if let Some(service_name) = name_part.strip_suffix("_repo_size_gb") {
|
} else if let Some(service_name) = name_part.strip_suffix("_repo_size_gb") {
|
||||||
@ -154,6 +137,7 @@ impl Widget for BackupWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BackupWidget {
|
impl BackupWidget {
|
||||||
|
#[allow(dead_code)]
|
||||||
fn update_from_metrics(&mut self, metrics: &[&Metric]) {
|
fn update_from_metrics(&mut self, metrics: &[&Metric]) {
|
||||||
debug!("Backup widget updating with {} metrics", metrics.len());
|
debug!("Backup widget updating with {} metrics", metrics.len());
|
||||||
for metric in metrics {
|
for metric in metrics {
|
||||||
@ -199,9 +183,6 @@ impl BackupWidget {
|
|||||||
"backup_last_run_timestamp" => {
|
"backup_last_run_timestamp" => {
|
||||||
self.last_run_timestamp = metric.value.as_i64();
|
self.last_run_timestamp = metric.value.as_i64();
|
||||||
}
|
}
|
||||||
"backup_total_services" => {
|
|
||||||
self.total_services = metric.value.as_i64();
|
|
||||||
}
|
|
||||||
"backup_total_repo_size_gb" => {
|
"backup_total_repo_size_gb" => {
|
||||||
self.total_repo_size_gb = metric.value.as_f32();
|
self.total_repo_size_gb = metric.value.as_f32();
|
||||||
}
|
}
|
||||||
@ -220,18 +201,6 @@ impl BackupWidget {
|
|||||||
"backup_disk_wear_percent" => {
|
"backup_disk_wear_percent" => {
|
||||||
self.backup_disk_wear_percent = metric.value.as_f32();
|
self.backup_disk_wear_percent = metric.value.as_f32();
|
||||||
}
|
}
|
||||||
"backup_disk_filesystem_label" => {
|
|
||||||
self.backup_disk_filesystem_label = Some(metric.value.as_string());
|
|
||||||
}
|
|
||||||
"backup_services_completed_count" => {
|
|
||||||
self.services_completed_count = metric.value.as_i64();
|
|
||||||
}
|
|
||||||
"backup_services_failed_count" => {
|
|
||||||
self.services_failed_count = metric.value.as_i64();
|
|
||||||
}
|
|
||||||
"backup_services_disabled_count" => {
|
|
||||||
self.services_disabled_count = metric.value.as_i64();
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
// Handle individual service metrics
|
// Handle individual service metrics
|
||||||
if let Some(service_name) = Self::extract_service_name(&metric.name) {
|
if let Some(service_name) = Self::extract_service_name(&metric.name) {
|
||||||
@ -243,7 +212,6 @@ impl BackupWidget {
|
|||||||
ServiceMetricData {
|
ServiceMetricData {
|
||||||
name: service_name,
|
name: service_name,
|
||||||
status: Status::Unknown,
|
status: Status::Unknown,
|
||||||
exit_code: None,
|
|
||||||
archive_count: None,
|
archive_count: None,
|
||||||
repo_size_gb: None,
|
repo_size_gb: None,
|
||||||
}
|
}
|
||||||
@ -252,8 +220,6 @@ impl BackupWidget {
|
|||||||
if metric.name.ends_with("_status") {
|
if metric.name.ends_with("_status") {
|
||||||
entry.status = metric.status;
|
entry.status = metric.status;
|
||||||
debug!("Set status for {}: {:?}", entry.name, entry.status);
|
debug!("Set status for {}: {:?}", entry.name, entry.status);
|
||||||
} else if metric.name.ends_with("_exit_code") {
|
|
||||||
entry.exit_code = metric.value.as_i64();
|
|
||||||
} else if metric.name.ends_with("_archive_count") {
|
} else if metric.name.ends_with("_archive_count") {
|
||||||
entry.archive_count = metric.value.as_i64();
|
entry.archive_count = metric.value.as_i64();
|
||||||
debug!(
|
debug!(
|
||||||
|
|||||||
@ -47,6 +47,7 @@ impl ServicesWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extract service name and determine if it's a parent or sub-service
|
/// Extract service name and determine if it's a parent or sub-service
|
||||||
|
#[allow(dead_code)]
|
||||||
fn extract_service_info(metric_name: &str) -> Option<(String, Option<String>)> {
|
fn extract_service_info(metric_name: &str) -> Option<(String, Option<String>)> {
|
||||||
if metric_name.starts_with("service_") {
|
if metric_name.starts_with("service_") {
|
||||||
if let Some(end_pos) = metric_name
|
if let Some(end_pos) = metric_name
|
||||||
@ -277,6 +278,7 @@ impl Widget for ServicesWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ServicesWidget {
|
impl ServicesWidget {
|
||||||
|
#[allow(dead_code)]
|
||||||
fn update_from_metrics(&mut self, metrics: &[&Metric]) {
|
fn update_from_metrics(&mut self, metrics: &[&Metric]) {
|
||||||
debug!("Services widget updating with {} metrics", metrics.len());
|
debug!("Services widget updating with {} metrics", metrics.len());
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,8 @@ pub struct SystemWidget {
|
|||||||
tmp_total_gb: Option<f32>,
|
tmp_total_gb: Option<f32>,
|
||||||
memory_status: Status,
|
memory_status: Status,
|
||||||
tmp_status: Status,
|
tmp_status: Status,
|
||||||
|
/// All tmpfs mounts (for auto-discovery support)
|
||||||
|
tmpfs_mounts: Vec<cm_dashboard_shared::TmpfsData>,
|
||||||
|
|
||||||
// Storage metrics (collected from disk metrics)
|
// Storage metrics (collected from disk metrics)
|
||||||
storage_pools: Vec<StoragePool>,
|
storage_pools: Vec<StoragePool>,
|
||||||
@ -50,7 +52,6 @@ struct StoragePool {
|
|||||||
used_gb: Option<f32>,
|
used_gb: Option<f32>,
|
||||||
total_gb: Option<f32>,
|
total_gb: Option<f32>,
|
||||||
status: Status,
|
status: Status,
|
||||||
health_status: Status, // Separate status for pool health vs usage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -88,6 +89,7 @@ impl SystemWidget {
|
|||||||
tmp_total_gb: None,
|
tmp_total_gb: None,
|
||||||
memory_status: Status::Unknown,
|
memory_status: Status::Unknown,
|
||||||
tmp_status: Status::Unknown,
|
tmp_status: Status::Unknown,
|
||||||
|
tmpfs_mounts: Vec::new(),
|
||||||
storage_pools: Vec::new(),
|
storage_pools: Vec::new(),
|
||||||
has_data: false,
|
has_data: false,
|
||||||
}
|
}
|
||||||
@ -121,20 +123,6 @@ impl SystemWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format /tmp usage
|
|
||||||
fn format_tmp_usage(&self) -> String {
|
|
||||||
match (self.tmp_usage_percent, self.tmp_used_gb, self.tmp_total_gb) {
|
|
||||||
(Some(pct), Some(used), Some(total)) => {
|
|
||||||
let used_str = if used < 0.1 {
|
|
||||||
format!("{:.0}B", used * 1024.0) // Show as MB if very small
|
|
||||||
} else {
|
|
||||||
format!("{:.1}GB", used)
|
|
||||||
};
|
|
||||||
format!("{:.0}% {}/{:.1}GB", pct, used_str, total)
|
|
||||||
}
|
|
||||||
_ => "—% —GB/—GB".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the current agent hash for rebuild completion detection
|
/// Get the current agent hash for rebuild completion detection
|
||||||
pub fn _get_agent_hash(&self) -> Option<&String> {
|
pub fn _get_agent_hash(&self) -> Option<&String> {
|
||||||
@ -166,7 +154,10 @@ impl Widget for SystemWidget {
|
|||||||
self.memory_total_gb = Some(memory.total_gb);
|
self.memory_total_gb = Some(memory.total_gb);
|
||||||
self.memory_status = Status::Ok;
|
self.memory_status = Status::Ok;
|
||||||
|
|
||||||
// Extract tmpfs data
|
// Store all tmpfs mounts for display
|
||||||
|
self.tmpfs_mounts = memory.tmpfs.clone();
|
||||||
|
|
||||||
|
// Extract tmpfs data (maintain backward compatibility for /tmp)
|
||||||
if let Some(tmp_data) = memory.tmpfs.iter().find(|t| t.mount == "/tmp") {
|
if let Some(tmp_data) = memory.tmpfs.iter().find(|t| t.mount == "/tmp") {
|
||||||
self.tmp_usage_percent = Some(tmp_data.usage_percent);
|
self.tmp_usage_percent = Some(tmp_data.usage_percent);
|
||||||
self.tmp_used_gb = Some(tmp_data.used_gb);
|
self.tmp_used_gb = Some(tmp_data.used_gb);
|
||||||
@ -196,7 +187,6 @@ impl SystemWidget {
|
|||||||
used_gb: None,
|
used_gb: None,
|
||||||
total_gb: None,
|
total_gb: None,
|
||||||
status: Status::Ok,
|
status: Status::Ok,
|
||||||
health_status: Status::Ok,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add drive info
|
// Add drive info
|
||||||
@ -278,40 +268,27 @@ impl SystemWidget {
|
|||||||
let pool_spans = StatusIcons::create_status_spans(pool.status.clone(), &pool_label);
|
let pool_spans = StatusIcons::create_status_spans(pool.status.clone(), &pool_label);
|
||||||
lines.push(Line::from(pool_spans));
|
lines.push(Line::from(pool_spans));
|
||||||
|
|
||||||
// Pool total usage line
|
// Show individual filesystems for physical drives (matching CLAUDE.md format)
|
||||||
if let (Some(usage), Some(used), Some(total)) = (pool.usage_percent, pool.used_gb, pool.total_gb) {
|
|
||||||
let usage_spans = vec![
|
|
||||||
Span::styled(" ├─ ", Typography::tree()),
|
|
||||||
Span::raw(" "),
|
|
||||||
];
|
|
||||||
let mut usage_line_spans = usage_spans;
|
|
||||||
usage_line_spans.extend(StatusIcons::create_status_spans(pool.status.clone(), &format!("Total: {}% {:.1}GB/{:.1}GB", usage as i32, used, total)));
|
|
||||||
lines.push(Line::from(usage_line_spans));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drive details for physical drives
|
|
||||||
if pool.pool_type.starts_with("drive") {
|
if pool.pool_type.starts_with("drive") {
|
||||||
for drive in &pool.drives {
|
// Show filesystem entries like: ├─ ● /: 55% 250.5GB/456.4GB
|
||||||
if drive.name == pool.name {
|
for (i, filesystem) in pool.filesystems.iter().enumerate() {
|
||||||
let mut drive_details = Vec::new();
|
let is_last = i == pool.filesystems.len() - 1;
|
||||||
if let Some(temp) = drive.temperature {
|
let tree_symbol = if is_last { " └─ " } else { " ├─ " };
|
||||||
drive_details.push(format!("T: {}°C", temp as i32));
|
|
||||||
}
|
|
||||||
if let Some(wear) = drive.wear_percent {
|
|
||||||
drive_details.push(format!("W: {}%", wear as i32));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !drive_details.is_empty() {
|
let fs_text = format!("{}: {:.0}% {:.1}GB/{:.1}GB",
|
||||||
let drive_text = format!("● {} {}", drive.name, drive_details.join(" "));
|
filesystem.mount_point,
|
||||||
let drive_spans = vec![
|
filesystem.usage_percent.unwrap_or(0.0),
|
||||||
Span::styled(" └─ ", Typography::tree()),
|
filesystem.used_gb.unwrap_or(0.0),
|
||||||
Span::raw(" "),
|
filesystem.total_gb.unwrap_or(0.0));
|
||||||
|
|
||||||
|
let mut fs_spans = vec![
|
||||||
|
Span::styled(tree_symbol, Typography::tree()),
|
||||||
];
|
];
|
||||||
let mut drive_line_spans = drive_spans;
|
fs_spans.extend(StatusIcons::create_status_spans(
|
||||||
drive_line_spans.extend(StatusIcons::create_status_spans(drive.status.clone(), &drive_text));
|
filesystem.status.clone(),
|
||||||
lines.push(Line::from(drive_line_spans));
|
&fs_text
|
||||||
}
|
));
|
||||||
}
|
lines.push(Line::from(fs_spans));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For mergerfs pools, show data drives and parity drives in tree structure
|
// For mergerfs pools, show data drives and parity drives in tree structure
|
||||||
@ -432,15 +409,29 @@ impl SystemWidget {
|
|||||||
);
|
);
|
||||||
lines.push(Line::from(memory_spans));
|
lines.push(Line::from(memory_spans));
|
||||||
|
|
||||||
let tmp_text = self.format_tmp_usage();
|
// Display all tmpfs mounts
|
||||||
let mut tmp_spans = vec![
|
for (i, tmpfs) in self.tmpfs_mounts.iter().enumerate() {
|
||||||
Span::styled(" └─ ", Typography::tree()),
|
let is_last = i == self.tmpfs_mounts.len() - 1;
|
||||||
|
let tree_symbol = if is_last { " └─ " } else { " ├─ " };
|
||||||
|
|
||||||
|
let usage_text = if tmpfs.total_gb > 0.0 {
|
||||||
|
format!("{:.0}% {:.1}GB/{:.1}GB",
|
||||||
|
tmpfs.usage_percent,
|
||||||
|
tmpfs.used_gb,
|
||||||
|
tmpfs.total_gb)
|
||||||
|
} else {
|
||||||
|
"— —/—".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut tmpfs_spans = vec![
|
||||||
|
Span::styled(tree_symbol, Typography::tree()),
|
||||||
];
|
];
|
||||||
tmp_spans.extend(StatusIcons::create_status_spans(
|
tmpfs_spans.extend(StatusIcons::create_status_spans(
|
||||||
self.tmp_status.clone(),
|
Status::Ok, // TODO: Calculate status based on usage_percent
|
||||||
&format!("/tmp: {}", tmp_text)
|
&format!("{}: {}", tmpfs.mount, usage_text)
|
||||||
));
|
));
|
||||||
lines.push(Line::from(tmp_spans));
|
lines.push(Line::from(tmpfs_spans));
|
||||||
|
}
|
||||||
|
|
||||||
// Storage section
|
// Storage section
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard-shared"
|
name = "cm-dashboard-shared"
|
||||||
version = "0.1.137"
|
version = "0.1.138"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user