feat(admin): make capacity calculation resilient when backend info is missing (#1560)

Signed-off-by: heihutu <30542132+heihutu@users.noreply.github.com>
Signed-off-by: majinghe <42570491+majinghe@users.noreply.github.com>
Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>
Co-authored-by: heihutu <30542132+heihutu@users.noreply.github.com>
This commit is contained in:
houseme
2026-01-20 13:03:09 +08:00
committed by GitHub
parent 14ce251e3b
commit 7c8fd8518f
2 changed files with 96 additions and 8 deletions
+89 -8
View File
@@ -1384,28 +1384,109 @@ impl SetDisks {
}
}
fn is_disk_online_state(state: &str) -> bool {
// The disk state strings are produced from rustfs_utils::os::get_drive_stats or DiskError::to_string().
// Conventionally, online is "ok"/"online" (may evolve). Be conservative:
// - Treat empty as unknown -> include it (to avoid dropping capacity).
// - Exclude explicit offline-ish states.
let s = state.trim().to_lowercase();
if s.is_empty() {
return true;
}
if s.contains("offline") {
return false;
}
if s.contains("not found") || s.contains("disk not found") {
return false;
}
true
}
fn fallback_total_capacity(disks: &[rustfs_madmin::Disk]) -> usize {
disks
.iter()
.filter(|d| is_disk_online_state(&d.state))
.map(|d| d.total_space as usize)
.sum()
}
fn fallback_free_capacity(disks: &[rustfs_madmin::Disk]) -> usize {
disks
.iter()
.filter(|d| is_disk_online_state(&d.state))
.map(|d| d.available_space as usize)
.sum()
}
pub fn get_total_usable_capacity(disks: &[rustfs_madmin::Disk], info: &rustfs_madmin::StorageInfo) -> usize {
let mut capacity = 0;
// If backend info is missing or inconsistent, do a safe fallback to avoid reporting nonsense.
if info.backend.standard_sc_data.is_empty() {
return fallback_total_capacity(disks);
}
let mut capacity = 0usize;
let mut matched_any = false;
for disk in disks.iter() {
if disk.pool_index < 0 || info.backend.standard_sc_data.len() <= disk.pool_index as usize {
if disk.pool_index < 0 {
continue;
}
if (disk.disk_index as usize) < info.backend.standard_sc_data[disk.pool_index as usize] {
let pool_idx = disk.pool_index as usize;
if info.backend.standard_sc_data.len() <= pool_idx {
continue;
}
let usable_disks_per_set = info.backend.standard_sc_data[pool_idx];
if usable_disks_per_set == 0 {
continue;
}
if (disk.disk_index as usize) < usable_disks_per_set {
matched_any = true;
capacity += disk.total_space as usize;
}
}
capacity
if matched_any {
capacity
} else {
// Even if standard_sc_data exists, it might not match disk indexes due to upstream bugs.
// Fallback to summing all online disks to prevent under-reporting.
fallback_total_capacity(disks)
}
}
pub fn get_total_usable_capacity_free(disks: &[rustfs_madmin::Disk], info: &rustfs_madmin::StorageInfo) -> usize {
let mut capacity = 0;
if info.backend.standard_sc_data.is_empty() {
return fallback_free_capacity(disks);
}
let mut capacity = 0usize;
let mut matched_any = false;
for disk in disks.iter() {
if disk.pool_index < 0 || info.backend.standard_sc_data.len() <= disk.pool_index as usize {
if disk.pool_index < 0 {
continue;
}
if (disk.disk_index as usize) < info.backend.standard_sc_data[disk.pool_index as usize] {
let pool_idx = disk.pool_index as usize;
if info.backend.standard_sc_data.len() <= pool_idx {
continue;
}
let usable_disks_per_set = info.backend.standard_sc_data[pool_idx];
if usable_disks_per_set == 0 {
continue;
}
if (disk.disk_index as usize) < usable_disks_per_set {
matched_any = true;
capacity += disk.available_space as usize;
}
}
capacity
if matched_any {
capacity
} else {
fallback_free_capacity(disks)
}
}
+7
View File
@@ -6817,10 +6817,17 @@ async fn get_storage_info(disks: &[Option<DiskStore>], eps: &[Endpoint]) -> rust
let mut disks = get_disks_info(disks, eps).await;
disks.sort_by(|a, b| a.total_space.cmp(&b.total_space));
// Provide minimal backend shape for callers. Do NOT guess parity here since it belongs to higher-level config.
// Missing/empty standard_sc_data will be handled by capacity fallback logic.
let drives_per_set = vec![eps.len()];
let total_sets = vec![1];
rustfs_madmin::StorageInfo {
disks,
backend: rustfs_madmin::BackendInfo {
backend_type: rustfs_madmin::BackendByte::Erasure,
drives_per_set,
total_sets,
..Default::default()
},
}