Treat historical SMART failures as resolved
This commit is contained in:
21
disks.go
21
disks.go
@@ -25,6 +25,7 @@ type DiskMetrics struct {
|
||||
SMARTStatus string `json:"smartStatus"`
|
||||
SMARTAvailable bool `json:"smartAvailable"`
|
||||
AttentionReasons []string `json:"attentionReasons"`
|
||||
SMARTHistory []string `json:"smartHistory"`
|
||||
Temperature *float64 `json:"temperatureCelsius"`
|
||||
PowerOnHours *uint64 `json:"powerOnHours"`
|
||||
WearUsedPercent *float64 `json:"wearUsedPercent"`
|
||||
@@ -33,6 +34,7 @@ type DiskMetrics struct {
|
||||
Uncorrectable uint64 `json:"uncorrectable"`
|
||||
CRCErrors uint64 `json:"crcErrors"`
|
||||
MediaErrors uint64 `json:"mediaErrors"`
|
||||
SMARTErrorCount uint64 `json:"smartErrorCount"`
|
||||
CounterChanges []string `json:"counterChanges"`
|
||||
LastSelfTest string `json:"lastSelfTest"`
|
||||
LastSelfTestAt string `json:"lastSelfTestAt"`
|
||||
@@ -203,7 +205,7 @@ func diskCounterChanges(old, current DiskMetrics) []string {
|
||||
checks := []struct {
|
||||
label string
|
||||
old, now uint64
|
||||
}{{"Reallocated вырос", old.Reallocated, current.Reallocated}, {"Pending вырос", old.Pending, current.Pending}, {"Uncorrectable вырос", old.Uncorrectable, current.Uncorrectable}, {"CRC-ошибки выросли", old.CRCErrors, current.CRCErrors}, {"Media errors выросли", old.MediaErrors, current.MediaErrors}}
|
||||
}{{"Reallocated вырос", old.Reallocated, current.Reallocated}, {"Pending вырос", old.Pending, current.Pending}, {"Uncorrectable вырос", old.Uncorrectable, current.Uncorrectable}, {"CRC-ошибки выросли", old.CRCErrors, current.CRCErrors}, {"Media errors выросли", old.MediaErrors, current.MediaErrors}, {"Ошибки SMART-журнала выросли", old.SMARTErrorCount, current.SMARTErrorCount}}
|
||||
var changes []string
|
||||
for _, check := range checks {
|
||||
if check.now > check.old {
|
||||
@@ -488,16 +490,23 @@ func applyATAAttributes(disk *DiskMetrics, smart smartctlJSON) {
|
||||
wear := float64(100 - attribute.Value)
|
||||
disk.WearUsedPercent = &wear
|
||||
}
|
||||
if attribute.WhenFailed != "" && attribute.WhenFailed != "-" {
|
||||
if strings.EqualFold(attribute.WhenFailed, "FAILING_NOW") {
|
||||
reason := attribute.Name + " вышел за порог"
|
||||
if attribute.Raw.String != "" {
|
||||
reason += " (" + attribute.Raw.String + ")"
|
||||
}
|
||||
disk.AttentionReasons = append(disk.AttentionReasons, reason)
|
||||
} else if attribute.WhenFailed != "" && attribute.WhenFailed != "-" {
|
||||
reason := attribute.Name + " ранее выходил за порог"
|
||||
if attribute.Raw.String != "" {
|
||||
reason += " (" + attribute.Raw.String + ")"
|
||||
}
|
||||
disk.SMARTHistory = append(disk.SMARTHistory, reason)
|
||||
}
|
||||
}
|
||||
if smart.ATAErrorLog.Summary.Count > 0 {
|
||||
disk.AttentionReasons = append(disk.AttentionReasons, "Ошибок в SMART-журнале: "+strconv.Itoa(smart.ATAErrorLog.Summary.Count))
|
||||
disk.SMARTErrorCount = uint64(smart.ATAErrorLog.Summary.Count)
|
||||
disk.SMARTHistory = append(disk.SMARTHistory, "Записей в SMART-журнале: "+strconv.Itoa(smart.ATAErrorLog.Summary.Count))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,12 +520,12 @@ func applySMARTExitStatus(disk *DiskMetrics, status int) {
|
||||
disk.AttentionReasons = append(disk.AttentionReasons, "Критический SMART-атрибут достиг порога")
|
||||
}
|
||||
if status&32 != 0 {
|
||||
disk.AttentionReasons = append(disk.AttentionReasons, "SMART-атрибут ранее находился ниже порога")
|
||||
disk.SMARTHistory = append(disk.SMARTHistory, "SMART-атрибут ранее находился ниже порога")
|
||||
}
|
||||
if status&64 != 0 {
|
||||
disk.AttentionReasons = append(disk.AttentionReasons, "В журнале SMART обнаружены ошибки")
|
||||
disk.SMARTHistory = append(disk.SMARTHistory, "В журнале SMART есть старые записи об ошибках")
|
||||
}
|
||||
if status&128 != 0 {
|
||||
disk.AttentionReasons = append(disk.AttentionReasons, "В журнале самотестирования есть ошибки")
|
||||
disk.SMARTHistory = append(disk.SMARTHistory, "В журнале самотестирования есть старые ошибки")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -68,3 +69,37 @@ func TestApplyATAWearAndReason(t *testing.T) {
|
||||
t.Fatalf("причина предупреждения не добавлена: %+v", disk)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHistoricalATAFailureDoesNotRequireAttention(t *testing.T) {
|
||||
var smart smartctlJSON
|
||||
attribute := struct {
|
||||
Name string `json:"name"`
|
||||
Value int `json:"value"`
|
||||
Threshold int `json:"thresh"`
|
||||
WhenFailed string `json:"when_failed"`
|
||||
Raw struct {
|
||||
String string `json:"string"`
|
||||
Value int64 `json:"value"`
|
||||
} `json:"raw"`
|
||||
}{Name: "Airflow_Temperature_Cel", WhenFailed: "In_the_past"}
|
||||
attribute.Raw.String = "46 (Min/Max 26/47 #15)"
|
||||
smart.ATAAttributes.Table = append(smart.ATAAttributes.Table, attribute)
|
||||
disk := DiskMetrics{}
|
||||
applyATAAttributes(&disk, smart)
|
||||
applySMARTExitStatus(&disk, 32|64|128)
|
||||
if len(disk.AttentionReasons) != 0 {
|
||||
t.Fatalf("историческая ошибка стала активной: %+v", disk.AttentionReasons)
|
||||
}
|
||||
if len(disk.SMARTHistory) != 4 {
|
||||
t.Fatalf("история SMART потеряна: %+v", disk.SMARTHistory)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSMARTErrorLogGrowthRequiresAttention(t *testing.T) {
|
||||
old := DiskMetrics{SMARTErrorCount: 2}
|
||||
current := DiskMetrics{SMARTErrorCount: 3}
|
||||
changes := diskCounterChanges(old, current)
|
||||
if len(changes) != 1 || !strings.Contains(changes[0], "2 → 3") {
|
||||
t.Fatalf("рост журнала не обнаружен: %+v", changes)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user