master
go 134 lines 3.03 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package adaptecraid
4
5 import (
6 "bufio"
7 "bytes"
8 "errors"
9 "fmt"
10 "strconv"
11 "strings"
12 )
13
14 type physicalDevice struct {
15 number string
16 state string
17 location string
18 vendor string
19 model string
20 smart string
21 smartWarnings string
22 powerState string
23 temperature string
24 }
25
26 func (c *Collector) collectPhysicalDevices(mx map[string]int64) error {
27 bs, err := c.exec.physicalDevicesInfo()
28 if err != nil {
29 return err
30 }
31
32 devices, err := parsePhysDevInfo(bs)
33 if err != nil {
34 return err
35 }
36
37 if len(devices) == 0 {
38 return errors.New("no physical devices found")
39 }
40
41 for _, pd := range devices {
42 if !c.pds[pd.number] {
43 c.pds[pd.number] = true
44 c.addPhysicalDeviceCharts(pd)
45 }
46
47 px := fmt.Sprintf("pd_%s_", pd.number)
48
49 // Unfortunately, all available states are unknown.
50 mx[px+"health_state_ok"] = 0
51 mx[px+"health_state_critical"] = 0
52 if isOkPDState(pd) {
53 mx[px+"health_state_ok"] = 1
54 } else {
55 mx[px+"health_state_critical"] = 1
56 }
57
58 if v, err := strconv.ParseInt(pd.smartWarnings, 10, 64); err == nil {
59 mx[px+"smart_warnings"] = v
60 }
61 if v, err := strconv.ParseInt(pd.temperature, 10, 64); err == nil {
62 mx[px+"temperature"] = v
63 }
64 }
65
66 return nil
67 }
68
69 func isOkPDState(pd *physicalDevice) bool {
70 // https://github.com/thomas-krenn/check_adaptec_raid/blob/a104fd88deede87df4f07403b44394bffb30c5c3/check_adaptec_raid#L455
71 switch pd.state {
72 case "Online",
73 "Global Hot-Spare",
74 "Dedicated Hot-Spare",
75 "Pooled Hot-Spare",
76 "Hot Spare",
77 "Ready",
78 "Online (JBOD)",
79 "Raw (Pass Through)":
80 return true
81 }
82 return false
83 }
84
85 func parsePhysDevInfo(bs []byte) (map[string]*physicalDevice, error) {
86 devices := make(map[string]*physicalDevice)
87
88 var pd *physicalDevice
89
90 sc := bufio.NewScanner(bytes.NewReader(bs))
91
92 for sc.Scan() {
93 line := strings.TrimSpace(sc.Text())
94
95 if after, ok := strings.CutPrefix(line, "Device #"); ok {
96 num := after
97 pd = &physicalDevice{number: num}
98 devices[num] = pd
99 continue
100 }
101
102 if pd == nil {
103 continue
104 }
105
106 switch {
107 case strings.HasPrefix(line, "Device is"):
108 // Skip non-disk entries such as "Device is an Enclosure Services Device".
109 if line != "Device is a Hard drive" {
110 delete(devices, pd.number)
111 pd = nil
112 }
113 case strings.HasPrefix(line, "State"):
114 pd.state = getColonSepValue(line)
115 case strings.HasPrefix(line, "Reported Location"):
116 pd.location = getColonSepValue(line)
117 case strings.HasPrefix(line, "Vendor"):
118 pd.vendor = getColonSepValue(line)
119 case strings.HasPrefix(line, "Model"):
120 pd.model = getColonSepValue(line)
121 case strings.HasPrefix(line, "S.M.A.R.T. warnings"):
122 pd.smartWarnings = getColonSepValue(line)
123 case strings.HasPrefix(line, "S.M.A.R.T."):
124 pd.smart = getColonSepValue(line)
125 case strings.HasPrefix(line, "Power State"):
126 pd.powerState = getColonSepValue(line)
127 case strings.HasPrefix(line, "Temperature"):
128 v := getColonSepValue(line) // '42 C/ 107 F' or 'Not Supported'
129 pd.temperature = strings.Fields(v)[0]
130 }
131 }
132
133 return devices, nil
134 }