Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 192eea6e0c | |||
| 43fb838c9b |
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.122"
|
version = "0.1.124"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -301,7 +301,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cm-dashboard-agent"
|
name = "cm-dashboard-agent"
|
||||||
version = "0.1.122"
|
version = "0.1.124"
|
||||||
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.122"
|
version = "0.1.124"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard-agent"
|
name = "cm-dashboard-agent"
|
||||||
version = "0.1.122"
|
version = "0.1.124"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ impl DiskCollector {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Convert numeric references to actual mount points if needed
|
// Convert numeric references to actual mount points if needed
|
||||||
let member_paths = if raw_paths.iter().any(|path| !path.starts_with('/')) {
|
let mut member_paths = if raw_paths.iter().any(|path| !path.starts_with('/')) {
|
||||||
// Handle numeric format like "1:2" by finding corresponding /mnt/disk* paths
|
// Handle numeric format like "1:2" by finding corresponding /mnt/disk* paths
|
||||||
self.resolve_numeric_mergerfs_paths(&raw_paths)?
|
self.resolve_numeric_mergerfs_paths(&raw_paths)?
|
||||||
} else {
|
} else {
|
||||||
@@ -218,6 +218,10 @@ impl DiskCollector {
|
|||||||
raw_paths
|
raw_paths
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For SnapRAID setups, also include parity drives as part of the pool
|
||||||
|
let snapraid_parity_paths = self.discover_snapraid_parity_drives()?;
|
||||||
|
member_paths.extend(snapraid_parity_paths);
|
||||||
|
|
||||||
// Categorize as data vs parity drives
|
// Categorize as data vs parity drives
|
||||||
let (data_drives, parity_drives) = match self.categorize_pool_drives(&member_paths) {
|
let (data_drives, parity_drives) = match self.categorize_pool_drives(&member_paths) {
|
||||||
Ok(drives) => drives,
|
Ok(drives) => drives,
|
||||||
@@ -240,6 +244,16 @@ impl DiskCollector {
|
|||||||
Ok(pools)
|
Ok(pools)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Discover SnapRAID parity drives
|
||||||
|
fn discover_snapraid_parity_drives(&self) -> Result<Vec<String>> {
|
||||||
|
let mount_devices = self.get_mount_devices()?;
|
||||||
|
let parity_paths: Vec<String> = mount_devices.keys()
|
||||||
|
.filter(|path| path.contains("parity"))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
Ok(parity_paths)
|
||||||
|
}
|
||||||
|
|
||||||
/// Categorize pool member drives as data vs parity
|
/// Categorize pool member drives as data vs parity
|
||||||
fn categorize_pool_drives(&self, member_paths: &[String]) -> Result<(Vec<DriveInfo>, Vec<DriveInfo>)> {
|
fn categorize_pool_drives(&self, member_paths: &[String]) -> Result<(Vec<DriveInfo>, Vec<DriveInfo>)> {
|
||||||
let mut data_drives = Vec::new();
|
let mut data_drives = Vec::new();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard"
|
name = "cm-dashboard"
|
||||||
version = "0.1.122"
|
version = "0.1.124"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -470,26 +470,7 @@ impl SystemWidget {
|
|||||||
);
|
);
|
||||||
lines.push(Line::from(pool_spans));
|
lines.push(Line::from(pool_spans));
|
||||||
|
|
||||||
// Pool health line (for multi-disk pools)
|
// Skip pool health line as discussed - removed
|
||||||
if pool.pool_type != "single" {
|
|
||||||
if let Some(health) = &pool.pool_health {
|
|
||||||
let health_text = match health.as_str() {
|
|
||||||
"healthy" => format!("Pool Status: {} Healthy",
|
|
||||||
if pool.drives.len() > 1 { format!("({} drives)", pool.drives.len()) } else { String::new() }),
|
|
||||||
"degraded" => "Pool Status: ⚠ Degraded".to_string(),
|
|
||||||
"critical" => "Pool Status: ✗ Critical".to_string(),
|
|
||||||
"rebuilding" => "Pool Status: ⟳ Rebuilding".to_string(),
|
|
||||||
_ => format!("Pool Status: ? {}", health),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut health_spans = vec![
|
|
||||||
Span::raw(" "),
|
|
||||||
Span::styled("├─ ", Typography::tree()),
|
|
||||||
];
|
|
||||||
health_spans.extend(StatusIcons::create_status_spans(pool.health_status.clone(), &health_text));
|
|
||||||
lines.push(Line::from(health_spans));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Total usage line (always show for pools)
|
// Total usage line (always show for pools)
|
||||||
let usage_text = match (pool.usage_percent, pool.used_gb, pool.total_gb) {
|
let usage_text = match (pool.usage_percent, pool.used_gb, pool.total_gb) {
|
||||||
@@ -512,7 +493,7 @@ impl SystemWidget {
|
|||||||
lines.push(Line::from(usage_spans));
|
lines.push(Line::from(usage_spans));
|
||||||
|
|
||||||
// Drive lines with enhanced grouping
|
// Drive lines with enhanced grouping
|
||||||
if pool.pool_type != "single" && pool.drives.len() > 1 {
|
if pool.pool_type.contains("mergerfs") && pool.drives.len() > 1 {
|
||||||
// Group drives by type for mergerfs pools
|
// Group drives by type for mergerfs pools
|
||||||
let (data_drives, parity_drives): (Vec<_>, Vec<_>) = pool.drives.iter().enumerate()
|
let (data_drives, parity_drives): (Vec<_>, Vec<_>) = pool.drives.iter().enumerate()
|
||||||
.partition(|(_, drive)| {
|
.partition(|(_, drive)| {
|
||||||
@@ -521,7 +502,7 @@ impl SystemWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Show data drives
|
// Show data drives
|
||||||
if !data_drives.is_empty() && pool.pool_type.contains("mergerfs") {
|
if !data_drives.is_empty() {
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
Span::raw(" "),
|
Span::raw(" "),
|
||||||
Span::styled("├─ ", Typography::tree()),
|
Span::styled("├─ ", Typography::tree()),
|
||||||
@@ -539,7 +520,7 @@ impl SystemWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show parity drives
|
// Show parity drives
|
||||||
if !parity_drives.is_empty() && pool.pool_type.contains("mergerfs") {
|
if !parity_drives.is_empty() {
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
Span::raw(" "),
|
Span::raw(" "),
|
||||||
Span::styled("└─ ", Typography::tree()),
|
Span::styled("└─ ", Typography::tree()),
|
||||||
@@ -554,13 +535,13 @@ impl SystemWidget {
|
|||||||
self.render_drive_line(&mut lines, drive, " ├─");
|
self.render_drive_line(&mut lines, drive, " ├─");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// Regular drive listing for non-mergerfs pools
|
} else if pool.pool_type != "single" && pool.drives.len() > 1 {
|
||||||
for (i, drive) in pool.drives.iter().enumerate() {
|
// Regular drive listing for non-mergerfs multi-drive pools
|
||||||
let is_last = i == pool.drives.len() - 1;
|
for (i, drive) in pool.drives.iter().enumerate() {
|
||||||
let tree_symbol = if is_last { "└─" } else { "├─" };
|
let is_last = i == pool.drives.len() - 1;
|
||||||
self.render_drive_line(&mut lines, drive, tree_symbol);
|
let tree_symbol = if is_last { "└─" } else { "├─" };
|
||||||
}
|
self.render_drive_line(&mut lines, drive, tree_symbol);
|
||||||
}
|
}
|
||||||
} else if pool.pool_type.starts_with("drive (") {
|
} else if pool.pool_type.starts_with("drive (") {
|
||||||
// Physical drive pools: show drive info + filesystem children
|
// Physical drive pools: show drive info + filesystem children
|
||||||
@@ -641,10 +622,12 @@ impl SystemWidget {
|
|||||||
if let Some(wear) = drive.wear_percent {
|
if let Some(wear) = drive.wear_percent {
|
||||||
drive_info.push(format!("W: {:.0}%", wear));
|
drive_info.push(format!("W: {:.0}%", wear));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always show drive name with info, or just name if no info available
|
||||||
let drive_text = if drive_info.is_empty() {
|
let drive_text = if drive_info.is_empty() {
|
||||||
drive.name.clone()
|
drive.name.clone()
|
||||||
} else {
|
} else {
|
||||||
format!("{} {}", drive.name, drive_info.join(" • "))
|
format!("{} {}", drive.name, drive_info.join(" "))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut drive_spans = vec![
|
let mut drive_spans = vec![
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard-shared"
|
name = "cm-dashboard-shared"
|
||||||
version = "0.1.122"
|
version = "0.1.124"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
Reference in New Issue
Block a user