master
go 133 lines 3.19 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package megacli
4
5 import (
6 "bufio"
7 "bytes"
8 "errors"
9 "fmt"
10 "strings"
11 )
12
13 type (
14 megaAdapter struct {
15 number string
16 name string
17 state string
18 physDrives map[string]*megaPhysDrive
19 }
20 megaPhysDrive struct {
21 adapterNumber string
22 number string
23 wwn string
24 slotNumber string
25 drivePosition string
26 pdType string
27 mediaErrorCount string
28 predictiveFailureCount string
29 }
30 )
31
32 var adapterStates = []string{
33 "optimal",
34 "degraded",
35 "partially_degraded",
36 "failed",
37 }
38
39 func (c *Collector) collectPhysDrives(mx map[string]int64) error {
40 bs, err := c.exec.physDrivesInfo()
41 if err != nil {
42 return err
43 }
44
45 adapters, err := parsePhysDrivesInfo(bs)
46 if err != nil {
47 return err
48 }
49 if len(adapters) == 0 {
50 return errors.New("no adapters found")
51 }
52
53 var drives int
54
55 for _, ad := range adapters {
56 if len(ad.physDrives) == 0 {
57 continue
58 }
59
60 if !c.adapters[ad.number] {
61 c.adapters[ad.number] = true
62 c.addAdapterCharts(ad)
63 }
64
65 px := fmt.Sprintf("adapter_%s_health_state_", ad.number)
66 for _, st := range adapterStates {
67 mx[px+st] = 0
68 }
69 st := strings.ReplaceAll(strings.ToLower(ad.state), " ", "_")
70 mx[px+st] = 1
71
72 for _, pd := range ad.physDrives {
73 if !c.adapters[pd.wwn] {
74 c.adapters[pd.wwn] = true
75 c.addPhysDriveCharts(pd)
76 }
77 drives++
78
79 px := fmt.Sprintf("phys_drive_%s_", pd.wwn)
80
81 writeInt(mx, px+"media_error_count", pd.mediaErrorCount)
82 writeInt(mx, px+"predictive_failure_count", pd.predictiveFailureCount)
83 }
84 }
85
86 c.Debugf("found %d adapters, %d physical drives", len(c.adapters), drives)
87
88 return nil
89 }
90
91 func parsePhysDrivesInfo(bs []byte) (map[string]*megaAdapter, error) {
92 adapters := make(map[string]*megaAdapter)
93
94 var ad *megaAdapter
95 var pd *megaPhysDrive
96
97 sc := bufio.NewScanner(bytes.NewReader(bs))
98
99 for sc.Scan() {
100 line := strings.TrimSpace(sc.Text())
101
102 switch {
103 case strings.HasPrefix(line, "Adapter #"):
104 idx := strings.TrimPrefix(line, "Adapter #")
105 ad = &megaAdapter{number: idx, physDrives: make(map[string]*megaPhysDrive)}
106 adapters[idx] = ad
107 case strings.HasPrefix(line, "Name") && ad != nil:
108 ad.name = getColonSepValue(line)
109 case strings.HasPrefix(line, "State") && ad != nil:
110 ad.state = getColonSepValue(line)
111 case strings.HasPrefix(line, "PD:") && ad != nil:
112 if parts := strings.Fields(line); len(parts) == 3 {
113 idx := parts[1]
114 pd = &megaPhysDrive{number: idx, adapterNumber: ad.number}
115 ad.physDrives[idx] = pd
116 }
117 case strings.HasPrefix(line, "Slot Number:") && pd != nil:
118 pd.slotNumber = getColonSepValue(line)
119 case strings.HasPrefix(line, "Drive's position:") && pd != nil:
120 pd.drivePosition = getColonSepValue(line)
121 case strings.HasPrefix(line, "WWN:") && pd != nil:
122 pd.wwn = getColonSepValue(line)
123 case strings.HasPrefix(line, "PD Type:") && pd != nil:
124 pd.pdType = getColonSepValue(line)
125 case strings.HasPrefix(line, "Media Error Count:") && pd != nil:
126 pd.mediaErrorCount = getColonSepNumValue(line)
127 case strings.HasPrefix(line, "Predictive Failure Count:") && pd != nil:
128 pd.predictiveFailureCount = getColonSepNumValue(line)
129 }
130 }
131
132 return adapters, nil
133 }