master
go 131 lines 3.44 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package powervault
4
5 // collectHardwareHealth classifies hardware components by health state.
6 // Uses cached discovery data — no API calls.
7 //
8 // health-numeric mapping: 0=OK, 1=Degraded, 2=Fault, 3=Unknown.
9 // Special cases:
10 // - Drives with usage-numeric=3 (Global Hot Spare) report health=Unknown but are functionally OK.
11 // - FRUs use fru-status-numeric: 0=OK, 1=Degraded, 2=Fault, 3=Unknown, 5=N/A (treated as OK).
12 // - Ports with health-numeric=4 (Not Available) are excluded from counts.
13 func (c *Collector) collectHardwareHealth() {
14 var ctrlOK, ctrlDeg, ctrlFault, ctrlUnk float64
15 var drvOK, drvDeg, drvFault, drvUnk float64
16 var fanOK, fanDeg, fanFault, fanUnk float64
17 var psuOK, psuDeg, psuFault, psuUnk float64
18 var fruOK, fruDeg, fruFault, fruUnk float64
19 var portOK, portDeg, portFault, portUnk float64
20
21 for _, ctrl := range c.discovered.controllers {
22 ok, deg, fault, unk := classifyHealth(ctrl.HealthNumeric)
23 ctrlOK += ok
24 ctrlDeg += deg
25 ctrlFault += fault
26 ctrlUnk += unk
27 }
28
29 for _, d := range c.discovered.drives {
30 // Global Hot Spare drives report health=Unknown but are functionally OK.
31 if d.UsageNumeric == 3 && d.HealthNumeric == 3 {
32 drvOK++
33 continue
34 }
35 ok, deg, fault, unk := classifyHealth(d.HealthNumeric)
36 drvOK += ok
37 drvDeg += deg
38 drvFault += fault
39 drvUnk += unk
40 }
41
42 for _, f := range c.discovered.fans {
43 ok, deg, fault, unk := classifyHealth(f.HealthNumeric)
44 fanOK += ok
45 fanDeg += deg
46 fanFault += fault
47 fanUnk += unk
48 }
49
50 for _, p := range c.discovered.psus {
51 ok, deg, fault, unk := classifyHealth(p.HealthNumeric)
52 psuOK += ok
53 psuDeg += deg
54 psuFault += fault
55 psuUnk += unk
56 }
57
58 for _, f := range c.discovered.frus {
59 ok, deg, fault, unk := classifyFRUHealth(f.FRUStatusNum)
60 fruOK += ok
61 fruDeg += deg
62 fruFault += fault
63 fruUnk += unk
64 }
65
66 for _, p := range c.discovered.ports {
67 // 4 = Not Available — exclude from health counts.
68 if p.HealthNumeric == 4 {
69 continue
70 }
71 ok, deg, fault, unk := classifyHealth(p.HealthNumeric)
72 portOK += ok
73 portDeg += deg
74 portFault += fault
75 portUnk += unk
76 }
77
78 hw := c.mx.hardware
79 hw.controllerOK.Observe(ctrlOK)
80 hw.controllerDegraded.Observe(ctrlDeg)
81 hw.controllerFault.Observe(ctrlFault)
82 hw.controllerUnknown.Observe(ctrlUnk)
83 hw.driveOK.Observe(drvOK)
84 hw.driveDegraded.Observe(drvDeg)
85 hw.driveFault.Observe(drvFault)
86 hw.driveUnknown.Observe(drvUnk)
87 hw.fanOK.Observe(fanOK)
88 hw.fanDegraded.Observe(fanDeg)
89 hw.fanFault.Observe(fanFault)
90 hw.fanUnknown.Observe(fanUnk)
91 hw.psuOK.Observe(psuOK)
92 hw.psuDegraded.Observe(psuDeg)
93 hw.psuFault.Observe(psuFault)
94 hw.psuUnknown.Observe(psuUnk)
95 hw.fruOK.Observe(fruOK)
96 hw.fruDegraded.Observe(fruDeg)
97 hw.fruFault.Observe(fruFault)
98 hw.fruUnknown.Observe(fruUnk)
99 hw.portOK.Observe(portOK)
100 hw.portDegraded.Observe(portDeg)
101 hw.portFault.Observe(portFault)
102 hw.portUnknown.Observe(portUnk)
103 }
104
105 // classifyHealth maps health-numeric (0=OK, 1=Degraded, 2=Fault, 3=Unknown).
106 func classifyHealth(h int) (ok, degraded, fault, unknown float64) {
107 switch h {
108 case 0:
109 return 1, 0, 0, 0
110 case 1:
111 return 0, 1, 0, 0
112 case 2:
113 return 0, 0, 1, 0
114 default:
115 return 0, 0, 0, 1
116 }
117 }
118
119 // classifyFRUHealth maps fru-status-numeric (0=OK, 1=Degraded, 2=Fault, 3=Unknown, 5=N/A→OK).
120 func classifyFRUHealth(h int) (ok, degraded, fault, unknown float64) {
121 switch h {
122 case 0, 5:
123 return 1, 0, 0, 0
124 case 1:
125 return 0, 1, 0, 0
126 case 2:
127 return 0, 0, 1, 0
128 default:
129 return 0, 0, 0, 1
130 }
131 }