master
go 364 lines 9.48 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package hpssa
4
5 import (
6 "bufio"
7 "bytes"
8 "fmt"
9 "strings"
10 )
11
12 type hpssaController struct {
13 model string
14 slot string
15 serialNumber string
16 controllerStatus string
17 cacheBoardPresent string
18 cacheStatus string
19 cacheRatio string
20 batteryCapacitorCount string
21 batteryCapacitorStatus string
22 controllerTemperatureC string
23 cacheModuleTemperatureC string
24 numberOfPorts string
25 driverName string
26 arrays map[string]*hpssaArray
27 unassignedDrives map[string]*hpssaPhysicalDrive
28 }
29
30 func (c *hpssaController) uniqueKey() string {
31 return fmt.Sprintf("%s/%s", c.model, c.slot)
32 }
33
34 type hpssaArray struct {
35 cntrl *hpssaController
36
37 id string
38 interfaceType string
39 unusedSpace string
40 usedSpace string
41 status string
42 arrayType string
43 logicalDrives map[string]*hpssaLogicalDrive
44 }
45
46 func (a *hpssaArray) uniqueKey() string {
47 return fmt.Sprintf("%s/%s/%s", a.cntrl.model, a.cntrl.slot, a.id)
48 }
49
50 type hpssaLogicalDrive struct {
51 cntrl *hpssaController
52 arr *hpssaArray
53
54 id string
55 size string
56 status string
57 diskName string
58 uniqueIdentifier string
59 logicalDriveLabel string
60 driveType string
61 physicalDrives map[string]*hpssaPhysicalDrive
62 }
63
64 func (ld *hpssaLogicalDrive) uniqueKey() string {
65 return fmt.Sprintf("%s/%s/%s/%s", ld.cntrl.model, ld.cntrl.slot, ld.arr.id, ld.id)
66 }
67
68 type hpssaPhysicalDrive struct {
69 cntrl *hpssaController
70 arr *hpssaArray
71 ld *hpssaLogicalDrive
72
73 location string // port:box:bay
74 status string
75 driveType string
76 interfaceType string
77 size string
78 serialNumber string
79 wwid string
80 model string
81 currentTemperatureC string
82 }
83
84 func (pd *hpssaPhysicalDrive) uniqueKey() string {
85 return fmt.Sprintf("%s/%s/%s/%s/%s", pd.cntrl.model, pd.cntrl.slot, pd.arrId(), pd.ldId(), pd.location)
86 }
87
88 func (pd *hpssaPhysicalDrive) arrId() string {
89 if pd.arr == nil {
90 return "na"
91 }
92 return pd.arr.id
93 }
94
95 func (pd *hpssaPhysicalDrive) ldId() string {
96 if pd.ld == nil {
97 return "na"
98 }
99 return pd.ld.id
100 }
101
102 func parseSsacliControllersInfo(data []byte) (map[string]*hpssaController, error) {
103 var (
104 cntrl *hpssaController
105 arr *hpssaArray
106 ld *hpssaLogicalDrive
107 pd *hpssaPhysicalDrive
108
109 line string
110 prevLine string
111 section string
112 unassigned bool
113 )
114
115 controllers := make(map[string]*hpssaController)
116
117 sc := bufio.NewScanner(bytes.NewReader(data))
118
119 for sc.Scan() {
120 prevLine = line
121 line = sc.Text()
122
123 switch {
124 case line == "":
125 section = ""
126 continue
127 case strings.HasPrefix(line, "HPE Smart Array"), strings.HasPrefix(line, "Smart Array"):
128 section = "controller"
129
130 v, err := parseControllerLine(line)
131 if err != nil {
132 return nil, err
133 }
134
135 cntrl = v
136 controllers[cntrl.slot] = cntrl
137
138 continue
139 case strings.HasPrefix(line, " Array:") && cntrl != nil:
140 section = "array"
141 unassigned = false
142
143 arr = parseArrayLine(line)
144 cntrl.arrays[arr.id] = arr
145
146 continue
147 case strings.HasPrefix(line, " Logical Drive:") && cntrl != nil && arr != nil:
148 section = "logical drive"
149
150 ld = parseLogicalDriveLine(line)
151 arr.logicalDrives[arr.id] = ld
152
153 continue
154 case strings.HasPrefix(line, " physicaldrive") && prevLine == "":
155 section = "physical drive"
156
157 if unassigned && cntrl == nil {
158 return nil, fmt.Errorf("unassigned drive but controller is nil (line '%s')", line)
159 }
160 if !unassigned && ld == nil {
161 return nil, fmt.Errorf("assigned drive but logical device is nil (line '%s')", line)
162 }
163
164 v, err := parsePhysicalDriveLine(line)
165 if err != nil {
166 return nil, err
167 }
168
169 pd = v
170 if unassigned {
171 cntrl.unassignedDrives[pd.location] = pd
172 } else {
173 ld.physicalDrives[pd.location] = pd
174 }
175
176 continue
177 case strings.HasPrefix(line, " Unassigned"):
178 unassigned = true
179 continue
180 }
181
182 switch section {
183 case "controller":
184 parseControllerSectionLine(line, cntrl)
185 case "array":
186 parseArraySectionLine(line, arr)
187 case "logical drive":
188 parseLogicalDriveSectionLine(line, ld)
189 case "physical drive":
190 parsePhysicalDriveSectionLine(line, pd)
191 }
192 }
193
194 if len(controllers) == 0 {
195 return nil, fmt.Errorf("no controllers found")
196 }
197
198 updateHpssaHierarchy(controllers)
199
200 return controllers, nil
201 }
202
203 func updateHpssaHierarchy(controllers map[string]*hpssaController) {
204 for _, cntrl := range controllers {
205 for _, pd := range cntrl.unassignedDrives {
206 pd.cntrl = cntrl
207 }
208 for _, arr := range cntrl.arrays {
209 arr.cntrl = cntrl
210 for _, ld := range arr.logicalDrives {
211 ld.cntrl = cntrl
212 ld.arr = arr
213 for _, pd := range ld.physicalDrives {
214 pd.cntrl = cntrl
215 pd.arr = arr
216 pd.ld = ld
217 }
218 }
219 }
220 }
221 }
222
223 func parseControllerLine(line string) (*hpssaController, error) {
224 parts := strings.Fields(strings.TrimPrefix(line, "Smart Array "))
225 if len(parts) < 4 {
226 return nil, fmt.Errorf("malformed Smart Array line: '%s'", line)
227 }
228
229 cntrl := &hpssaController{
230 model: parts[0],
231 slot: parts[3],
232 arrays: make(map[string]*hpssaArray),
233 unassignedDrives: make(map[string]*hpssaPhysicalDrive),
234 }
235
236 return cntrl, nil
237 }
238
239 func parseArrayLine(line string) *hpssaArray {
240 arr := &hpssaArray{
241 id: getColonSepValue(line),
242 logicalDrives: make(map[string]*hpssaLogicalDrive),
243 }
244
245 return arr
246 }
247
248 func parseLogicalDriveLine(line string) *hpssaLogicalDrive {
249 ld := &hpssaLogicalDrive{
250 id: getColonSepValue(line),
251 physicalDrives: make(map[string]*hpssaPhysicalDrive),
252 }
253
254 return ld
255 }
256
257 func parsePhysicalDriveLine(line string) (*hpssaPhysicalDrive, error) {
258 parts := strings.Fields(strings.TrimSpace(line))
259 if len(parts) != 2 {
260 return nil, fmt.Errorf("malformed physicaldrive line: '%s'", line)
261 }
262
263 pd := &hpssaPhysicalDrive{
264 location: parts[1],
265 }
266
267 return pd, nil
268 }
269
270 func parseControllerSectionLine(line string, cntrl *hpssaController) {
271 indent := strings.Repeat(" ", 3)
272
273 switch {
274 case strings.HasPrefix(line, indent+"Serial Number:"):
275 cntrl.serialNumber = getColonSepValue(line)
276 case strings.HasPrefix(line, indent+"Controller Status:"):
277 cntrl.controllerStatus = getColonSepValue(line)
278 case strings.HasPrefix(line, indent+"Cache Board Present:"):
279 cntrl.cacheBoardPresent = getColonSepValue(line)
280 case strings.HasPrefix(line, indent+"Cache Status:"):
281 cntrl.cacheStatus = getColonSepValue(line)
282 case strings.HasPrefix(line, indent+"Cache Ratio:"):
283 cntrl.cacheRatio = getColonSepValue(line)
284 case strings.HasPrefix(line, indent+"Controller Temperature (C):"):
285 cntrl.controllerTemperatureC = getColonSepValue(line)
286 case strings.HasPrefix(line, indent+"Cache Module Temperature (C):"):
287 cntrl.cacheModuleTemperatureC = getColonSepValue(line)
288 case strings.HasPrefix(line, indent+"Number of Ports:"):
289 cntrl.numberOfPorts = getColonSepValue(line)
290 case strings.HasPrefix(line, indent+"Driver Name:"):
291 cntrl.driverName = getColonSepValue(line)
292 case strings.HasPrefix(line, indent+"Battery/Capacitor Count:"):
293 cntrl.batteryCapacitorCount = getColonSepValue(line)
294 case strings.HasPrefix(line, indent+"Battery/Capacitor Status:"):
295 cntrl.batteryCapacitorStatus = getColonSepValue(line)
296 }
297 }
298
299 func parseArraySectionLine(line string, arr *hpssaArray) {
300 indent := strings.Repeat(" ", 6)
301
302 switch {
303 case strings.HasPrefix(line, indent+"Interface Type:"):
304 arr.interfaceType = getColonSepValue(line)
305 case strings.HasPrefix(line, indent+"Unused Space:"):
306 arr.unusedSpace = getColonSepValue(line)
307 case strings.HasPrefix(line, indent+"Used Space:"):
308 arr.usedSpace = getColonSepValue(line)
309 case strings.HasPrefix(line, indent+"Status:"):
310 arr.status = getColonSepValue(line)
311 case strings.HasPrefix(line, indent+"Array Type:"):
312 arr.arrayType = getColonSepValue(line)
313 }
314 }
315
316 func parseLogicalDriveSectionLine(line string, ld *hpssaLogicalDrive) {
317 indent := strings.Repeat(" ", 9)
318
319 switch {
320 case strings.HasPrefix(line, indent+"Size:"):
321 ld.size = getColonSepValue(line)
322 case strings.HasPrefix(line, indent+"Status:"):
323 ld.status = getColonSepValue(line)
324 case strings.HasPrefix(line, indent+"Disk Name:"):
325 ld.diskName = getColonSepValue(line)
326 case strings.HasPrefix(line, indent+"Unique Identifier:"):
327 ld.uniqueIdentifier = getColonSepValue(line)
328 case strings.HasPrefix(line, indent+"Logical Drive Label:"):
329 ld.logicalDriveLabel = getColonSepValue(line)
330 case strings.HasPrefix(line, indent+"Drive Type:"):
331 ld.driveType = getColonSepValue(line)
332 }
333 }
334
335 func parsePhysicalDriveSectionLine(line string, pd *hpssaPhysicalDrive) {
336 indent := strings.Repeat(" ", 9)
337
338 switch {
339 case strings.HasPrefix(line, indent+"Status:"):
340 pd.status = getColonSepValue(line)
341 case strings.HasPrefix(line, indent+"Drive Type:"):
342 pd.driveType = getColonSepValue(line)
343 case strings.HasPrefix(line, indent+"Interface Type:"):
344 pd.interfaceType = getColonSepValue(line)
345 case strings.HasPrefix(line, indent+"Size:"):
346 pd.size = getColonSepValue(line)
347 case strings.HasPrefix(line, indent+"Serial Number:"):
348 pd.serialNumber = getColonSepValue(line)
349 case strings.HasPrefix(line, indent+"WWID:"):
350 pd.wwid = getColonSepValue(line)
351 case strings.HasPrefix(line, indent+"Model:"):
352 pd.model = getColonSepValue(line)
353 case strings.HasPrefix(line, indent+"Current Temperature (C):"):
354 pd.currentTemperatureC = getColonSepValue(line)
355 }
356 }
357
358 func getColonSepValue(line string) string {
359 _, after, ok := strings.Cut(line, ":")
360 if !ok {
361 return ""
362 }
363 return strings.TrimSpace(after)
364 }