DiskHealth/smartdiskinfo.go

136 lines
3.7 KiB
Go
Raw Permalink Normal View History

2022-04-15 15:16:15 +00:00
package main
import (
"encoding/json"
"fmt"
)
type DiskInfo struct {
ModelFamily string `json:"model_family"`
ModelName string `json:"model_name"`
SerialNumber string `json:"serial_number"`
RotationRate uint `json:"rotation_rate"`
}
func getDiskInfo(diskpath string) *DiskInfo {
rawsmart := execSystem("smartctl", "-i", "-json", diskpath)
if rawsmart == nil {
fmt.Println("error while getting smart info")
}
var info DiskInfo
// decoding country1 struct
// from json format
err := json.Unmarshal(rawsmart, &info)
if err != nil {
fmt.Println(err)
}
return &info
}
type Item struct {
Name string `json:"name"`
Raw struct {
2022-05-05 10:17:21 +00:00
Value int64 `json:"value"`
2022-04-15 15:16:15 +00:00
} `json:"raw"`
}
2022-05-05 09:56:08 +00:00
type RawAttr struct {
AtaSmartAttr struct {
Table []Item `json:"table"`
} `json:"ata_smart_attributes"`
}
func getSmartValues(diskpath string) *RawAttr {
2022-04-15 15:16:15 +00:00
rawsmart := execSystem("smartctl", "-A", "-json", diskpath)
if rawsmart == nil {
fmt.Println("error while getting smart info")
}
var rawattr RawAttr
err := json.Unmarshal(rawsmart, &rawattr)
if err != nil {
fmt.Println(err)
}
2022-05-05 09:56:08 +00:00
return &rawattr
}
func getTemp(diskpath string) (uint32, uint32, uint32) {
rawattr := getSmartValues(diskpath)
temp := getItemValue(rawattr.AtaSmartAttr.Table, "Temperature_Celsius")
// temp value is storead as following:
// 4bytes 4bytes 4bytes
// [----------------|----------------|---------------]
// max temp min temp curr temp
max := temp >> 32
min := (temp & 0xff00ff) >> 16
currtemp := temp & 0x0000ff
return uint32(max), uint32(min), uint32(currtemp)
}
2022-05-05 10:41:16 +00:00
func printAligned(paramname string, valuestr string) {
fmt.Printf("%-24v%s\n", paramname+":", valuestr)
}
2022-05-05 09:56:08 +00:00
func checkSmartAttributes(diskpath string, isSeagate bool, isHdd bool) {
rawattr := getSmartValues(diskpath)
2022-04-15 15:16:15 +00:00
rrerrrate := getItemValue(rawattr.AtaSmartAttr.Table, "Raw_Read_Error_Rate")
// seagate hdds have a special way to represent their error rates
if isSeagate {
// [--16bits---|---32bits---]
// error count| operation count
rrerrrate = rrerrrate >> 32
}
2022-05-05 10:41:16 +00:00
printAligned("Raw_Read_Error_Rate", evalStrZero(rrerrrate))
printAligned("Reallocated_Sector_Ct", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Reallocated_Sector_Ct")))
2022-04-15 15:16:15 +00:00
2022-05-05 10:17:21 +00:00
// todo further investigate if this smart attributes can occur within hdds/ssds
// https://www.backblaze.com/blog/what-smart-stats-indicate-hard-drive-failures/
2022-05-05 10:41:16 +00:00
printAligned("Reported_Uncorrect", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Reported_Uncorrect")))
printAligned("Command_Timeout", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Command_Timeout")))
printAligned("Current_Pending_Sector", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Current_Pending_Sector")))
printAligned("Offline_Uncorrectable", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Offline_Uncorrectable")))
2022-05-05 10:17:21 +00:00
2022-05-05 10:41:16 +00:00
// there are some additinoal hdd only smart values
2022-04-15 15:16:15 +00:00
if isHdd {
rrerrrate = getItemValue(rawattr.AtaSmartAttr.Table, "Seek_Error_Rate")
if isSeagate {
// [--16bits---|---32bits---]
// error count| operation count
rrerrrate = rrerrrate >> 32
}
2022-05-05 10:41:16 +00:00
printAligned("Seek_Error_Rate", evalStrZero(rrerrrate))
printAligned("Spin_Retry_Count", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Spin_Retry_Count")))
printAligned("Spin_Up_Time", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Spin_Up_Time")))
} else {
// todo ssd only parameters
2022-04-15 15:16:15 +00:00
}
}
2022-05-05 10:17:21 +00:00
func evalStrZero(nr int64) string {
if nr < 0 {
return "N/A"
} else if nr == 0 {
2022-05-05 10:41:16 +00:00
return "PASS"
2022-04-15 15:16:15 +00:00
} else {
2022-05-05 10:41:16 +00:00
return fmt.Sprintf("FAIL :: raw value=%d", nr)
2022-04-15 15:16:15 +00:00
}
}
2022-05-05 10:17:21 +00:00
func getItemValue(arr []Item, name string) int64 {
2022-04-15 15:16:15 +00:00
for i := range arr {
if arr[i].Name == name {
return arr[i].Raw.Value
}
}
2022-05-05 10:17:21 +00:00
return -1
2022-04-15 15:16:15 +00:00
}