fix #3433: use PVE's wearout logic in PBS

in PVE, the logic how wearout gets read from the smartctl output was
changed from a vendor -> id map to a sorted list of specific
attribute field names.

copy that list to pbs (in the same order), and use that to get the
wearout

in the future we might want to split the disk logic into its own crate
and reuse it in pve

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
Dominik Csapak 2021-06-04 15:59:46 +02:00 committed by Dietmar Maurer
parent ddfa4d679a
commit f960fc3b6f
1 changed files with 33 additions and 24 deletions

View File

@ -1,3 +1,6 @@
use std::collections::{HashMap, HashSet};
use lazy_static::lazy_static;
use anyhow::{bail, Error}; use anyhow::{bail, Error};
use ::serde::{Deserialize, Serialize}; use ::serde::{Deserialize, Serialize};
@ -95,10 +98,10 @@ pub fn get_smart_data(
let mut wearout = None; let mut wearout = None;
let mut attributes = Vec::new(); let mut attributes = Vec::new();
let mut wearout_candidates = HashMap::new();
// ATA devices // ATA devices
if let Some(list) = output["ata_smart_attributes"]["table"].as_array() { if let Some(list) = output["ata_smart_attributes"]["table"].as_array() {
let wearout_id = lookup_vendor_wearout_id(disk);
for item in list { for item in list {
let id = match item["id"].as_u64() { let id = match item["id"].as_u64() {
Some(id) => id, Some(id) => id,
@ -135,8 +138,8 @@ pub fn get_smart_data(
None => continue, // skip attributes without threshold entry None => continue, // skip attributes without threshold entry
}; };
if id == wearout_id { if WEAROUT_FIELD_NAMES.contains(&name as &str) {
wearout = Some(normalized); wearout_candidates.insert(name.clone(), normalized);
} }
attributes.push(SmartAttribute { attributes.push(SmartAttribute {
@ -151,6 +154,15 @@ pub fn get_smart_data(
} }
} }
if !wearout_candidates.is_empty() {
for field in WEAROUT_FIELD_ORDER {
if let Some(value) = wearout_candidates.get(field as &str) {
wearout = Some(*value);
break;
}
}
}
// NVME devices // NVME devices
if let Some(list) = output["nvme_smart_health_information_log"].as_object() { if let Some(list) = output["nvme_smart_health_information_log"].as_object() {
for (name, value) in list { for (name, value) in list {
@ -186,27 +198,24 @@ pub fn get_smart_data(
Ok(SmartData { status, wearout, attributes }) Ok(SmartData { status, wearout, attributes })
} }
fn lookup_vendor_wearout_id(disk: &super::Disk) -> u64 { static WEAROUT_FIELD_ORDER: &[&'static str] = &[
"Media_Wearout_Indicator",
"SSD_Life_Left",
"Wear_Leveling_Count",
"Perc_Write/Erase_Ct_BC",
"Perc_Rated_Life_Remain",
"Remaining_Lifetime_Perc",
"Percent_Lifetime_Remain",
"Lifetime_Left",
"PCT_Life_Remaining",
"Lifetime_Remaining",
"Percent_Life_Remaining",
"Percent_Lifetime_Used",
"Perc_Rated_Life_Used"
];
static VENDOR_MAP: &[(&str, u64)] = &[ lazy_static! {
("kingston", 231), static ref WEAROUT_FIELD_NAMES: HashSet<&'static str> = {
("samsung", 177), WEAROUT_FIELD_ORDER.iter().cloned().collect()
("intel", 233),
("sandisk", 233),
("crucial", 202),
];
let result = 233; // default
let model = match disk.model() {
Some(model) => model.to_string_lossy().to_lowercase(),
None => return result,
}; };
for (vendor, attr_id) in VENDOR_MAP {
if model.contains(vendor) {
return *attr_id;
}
}
result
} }