master
go 237 lines 5.72 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package storcli
4
5 import (
6 "encoding/json"
7 "errors"
8 "fmt"
9 "strconv"
10 "strings"
11 )
12
13 type drivesInfoResponse struct {
14 Controllers []struct {
15 CommandStatus struct {
16 Controller int `json:"Controller"`
17 Status string `json:"Status"`
18 } `json:"Command Status"`
19 ResponseData map[string]json.RawMessage `json:"Response Data"`
20 } `json:"Controllers"`
21 }
22
23 type (
24 driveInfo struct {
25 EIDSlt string `json:"EID:Slt"`
26 //DID int `json:"DID"`
27 //State string `json:"State"`
28 //DG int `json:"DG"` // FIX: can be integer or "-"
29 //Size string `json:"Size"`
30 //Intf string `json:"Intf"`
31 Med string `json:"Med"`
32 //SED string `json:"SED"`
33 //PI string `json:"PI"`
34 //SeSz string `json:"SeSz"`
35 //Model string `json:"Model"`
36 //Sp string `json:"Sp"`
37 //Type string `json:"Type"`
38 }
39 driveState struct {
40 MediaErrorCount storNumber `json:"Media Error Count"`
41 OtherErrorCount storNumber `json:"Other Error Count"`
42 DriveTemperature string `json:"Drive Temperature"`
43 PredictiveFailureCount storNumber `json:"Predictive Failure Count"`
44 SmartAlertFlagged string `json:"S.M.A.R.T alert flagged by drive"`
45 }
46 driveAttrs struct {
47 WWN string `json:"WWN"`
48 DeviceSpeed string `json:"Device Speed"`
49 LinkSpeed string `json:"Link Speed"`
50 }
51 )
52
53 type storNumber string // some int values can be 'N/A'
54
55 func (n *storNumber) UnmarshalJSON(b []byte) error { *n = storNumber(b); return nil }
56
57 func (c *Collector) collectMegaRaidDrives(mx map[string]int64, resp *drivesInfoResponse) error {
58 if resp == nil {
59 return nil
60 }
61
62 for _, cntrl := range resp.Controllers {
63 var ids []string
64 for k := range cntrl.ResponseData {
65 if !strings.HasSuffix(k, "Detailed Information") {
66 continue
67 }
68 parts := strings.Fields(k) // Drive /c0/e252/s0 - Detailed Information
69 if len(parts) < 2 {
70 continue
71 }
72 id := parts[1]
73 if strings.IndexByte(id, '/') == -1 {
74 continue
75 }
76 ids = append(ids, id)
77 }
78
79 cntrlIdx := cntrl.CommandStatus.Controller
80
81 for _, id := range ids {
82 info, err := getDriveInfo(cntrl.ResponseData, id)
83 if err != nil {
84 return err
85 }
86 data, err := getDriveDetailedInfo(cntrl.ResponseData, id)
87 if err != nil {
88 return err
89 }
90 state, err := getDriveState(data, id)
91 if err != nil {
92 return err
93 }
94 attrs, err := getDriveAttrs(data, id)
95 if err != nil {
96 return err
97 }
98
99 if attrs.WWN == "" {
100 continue
101 }
102
103 if !c.drives[attrs.WWN] {
104 c.drives[attrs.WWN] = true
105 c.addPhysDriveCharts(cntrlIdx, info, state, attrs)
106 }
107
108 px := fmt.Sprintf("phys_drive_%s_cntrl_%d_", attrs.WWN, cntrlIdx)
109
110 if v, ok := parseInt(string(state.MediaErrorCount)); ok {
111 mx[px+"media_error_count"] = v
112 }
113 if v, ok := parseInt(string(state.OtherErrorCount)); ok {
114 mx[px+"other_error_count"] = v
115 }
116 if v, ok := parseInt(string(state.PredictiveFailureCount)); ok {
117 mx[px+"predictive_failure_count"] = v
118 }
119 if v, ok := parseInt(getTemperature(state.DriveTemperature)); ok {
120 mx[px+"temperature"] = v
121 }
122 for _, st := range []string{"active", "inactive"} {
123 mx[px+"smart_alert_status_"+st] = 0
124 }
125 if state.SmartAlertFlagged == "Yes" {
126 mx[px+"smart_alert_status_active"] = 1
127 } else {
128 mx[px+"smart_alert_status_inactive"] = 1
129 }
130 }
131 }
132
133 return nil
134 }
135
136 func (c *Collector) queryDrivesInfo() (*drivesInfoResponse, error) {
137 bs, err := c.exec.drivesInfo()
138 if err != nil {
139 return nil, err
140 }
141
142 if len(bs) == 0 {
143 return nil, errors.New("empty response")
144 }
145
146 var resp drivesInfoResponse
147 if err := json.Unmarshal(bs, &resp); err != nil {
148 return nil, err
149 }
150
151 if len(resp.Controllers) == 0 {
152 return nil, errors.New("no controllers found")
153 }
154 if st := resp.Controllers[0].CommandStatus.Status; st != "Success" {
155 return nil, fmt.Errorf("command status error: %s", st)
156 }
157
158 return &resp, nil
159 }
160
161 func getDriveInfo(respData map[string]json.RawMessage, id string) (*driveInfo, error) {
162 k := fmt.Sprintf("Drive %s", id)
163 raw, ok := respData[k]
164 if !ok {
165 return nil, fmt.Errorf("drive info not found for '%s'", id)
166 }
167
168 var drive []driveInfo
169 if err := json.Unmarshal(raw, &drive); err != nil {
170 return nil, err
171 }
172
173 if len(drive) == 0 {
174 return nil, fmt.Errorf("drive info not found for '%s'", id)
175 }
176
177 return &drive[0], nil
178 }
179
180 func getDriveDetailedInfo(respData map[string]json.RawMessage, id string) (map[string]json.RawMessage, error) {
181 k := fmt.Sprintf("Drive %s - Detailed Information", id)
182 raw, ok := respData[k]
183 if !ok {
184 return nil, fmt.Errorf("drive detailed info not found for '%s'", id)
185 }
186
187 var info map[string]json.RawMessage
188 if err := json.Unmarshal(raw, &info); err != nil {
189 return nil, err
190 }
191
192 return info, nil
193 }
194
195 func getDriveState(driveDetailedInfo map[string]json.RawMessage, id string) (*driveState, error) {
196 k := fmt.Sprintf("Drive %s State", id)
197 raw, ok := driveDetailedInfo[k]
198 if !ok {
199 return nil, fmt.Errorf("drive detailed info state not found for '%s'", id)
200 }
201
202 var state driveState
203 if err := json.Unmarshal(raw, &state); err != nil {
204 return nil, err
205 }
206
207 return &state, nil
208 }
209
210 func getDriveAttrs(driveDetailedInfo map[string]json.RawMessage, id string) (*driveAttrs, error) {
211 k := fmt.Sprintf("Drive %s Device attributes", id)
212 raw, ok := driveDetailedInfo[k]
213 if !ok {
214 return nil, fmt.Errorf("drive detailed info state not found for '%s'", id)
215 }
216
217 var state driveAttrs
218 if err := json.Unmarshal(raw, &state); err != nil {
219 return nil, err
220 }
221
222 return &state, nil
223 }
224
225 func getTemperature(temp string) string {
226 // ' 28C (82.40 F)' (drive) or '33C' (bbu)
227 before, _, ok := strings.Cut(temp, "C")
228 if !ok {
229 return ""
230 }
231 return strings.TrimSpace(before)
232 }
233
234 func parseInt(s string) (int64, bool) {
235 i, err := strconv.ParseInt(s, 10, 64)
236 return i, err == nil
237 }