-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsmartctl.go
96 lines (79 loc) · 2.13 KB
/
smartctl.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package main
import (
"encoding/json"
"os/exec"
"strconv"
"strings"
"github.com/tidwall/gjson"
)
func Smartctl(args ...string) gjson.Result {
args = append([]string{"--json=c"}, args...)
cmd := exec.Command("smartctl", args...)
output, _ := cmd.Output()
return gjson.ParseBytes(output)
}
type Device struct {
Name string
Type string
Protocol string
}
func GetDevices() []*Device {
devices := []*Device{}
Smartctl("--scan-open").Get("devices").ForEach(func(k, v gjson.Result) bool {
device := &Device{}
json.Unmarshal([]byte(v.Raw), device)
devices = append(devices, device)
return true
})
return devices
}
type Result struct {
ModelName string `json:"model_name"`
SerialNumber string `json:"serial_number"`
FirmwareVersion string `json:"firmware_version"`
Passed bool
Attributes map[string]float64
}
func GetAll(dev *Device) *Result {
j := Smartctl("--xall", dev.Name)
r := &Result{}
json.Unmarshal([]byte(j.Raw), r)
r.Passed = j.Get("smart_status.passed").Bool()
r.Attributes = map[string]float64{}
r.Attributes["temperature"] = j.Get("temperature.current").Float()
r.Attributes["power_on_hours"] = j.Get("power_on_time.hours").Float()
switch dev.Type {
case "nvme":
j.Get("nvme_smart_health_information_log").ForEach(func(k, v gjson.Result) bool {
if v.Type == gjson.JSON {
return true
}
r.Attributes[k.Str] = v.Float()
return true
})
case "sat":
j.Get("ata_smart_attributes.table").ForEach(func(k, v gjson.Result) bool {
name := strings.ReplaceAll(strings.ToLower(v.Get("name").Str), "-", "_")
rawStr := v.Get("raw.string").Str
value := float64(v.Get("value").Int())
rawFloat, err := strconv.ParseFloat(rawStr, 64)
if err != nil {
idx := strings.IndexByte(rawStr, '(')
if idx == -1 {
return true
}
rawStr = strings.TrimSpace(rawStr[:idx])
if rawFloat, err = strconv.ParseFloat(rawStr, 64); err != nil {
return true
}
}
r.Attributes[name] = rawFloat
r.Attributes[name+"_value"] = value
if name == "total_lbas_written" {
r.Attributes["data_units_written"] = rawFloat
}
return true
})
}
return r
}