rewrite megacli in go (#17410)
Ilya Mashchenko committed
Apr 16, 2024 at 17:38 UTC
7102eb799ca753ff9309fc271a22acafbc2083b1
20 files changed
+1806
-5
src/go/collectors/go.d.plugin/README.md
+1
@@ -86,6 +86,7 @@ see the appropriate collector readme.
86
| [logind](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/logind) | systemd-logind |
87
| [logstash](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/logstash) | Logstash |
88
| [lvm](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/lvm) | LVM logical volumes |
89
+| [megacli](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/megacli) | MegaCli Hardware Raid |
90
| [mongoDB](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/mongodb) | MongoDB |
91
| [mysql](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/mysql) | MySQL |
92
| [nginx](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/nginx) | NGINX |
src/go/collectors/go.d.plugin/config/go.d.conf
+1
@@ -48,6 +48,7 @@ modules:
48
# logind: yes
49
# logstash: yes
50
# lvm: yes
51
+# megacli: yes
52
# mongodb: yes
53
# mysql: yes
54
# nginx: yes
src/go/collectors/go.d.plugin/config/go.d/megacli.conf
new
+5
@@ -0,0 +1,5 @@
1
+## All available configuration options, their descriptions and default values:
2
+## https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/megacli#readme
3
+
4
+jobs:
5
+ - name: megacli
src/go/collectors/go.d.plugin/modules/init.go
+1
@@ -39,6 +39,7 @@ import (
39
_ "github.com/netdata/netdata/go/go.d.plugin/modules/logind"
40
_ "github.com/netdata/netdata/go/go.d.plugin/modules/logstash"
41
_ "github.com/netdata/netdata/go/go.d.plugin/modules/lvm"
42
+ _ "github.com/netdata/netdata/go/go.d.plugin/modules/megacli"
43
_ "github.com/netdata/netdata/go/go.d.plugin/modules/mongodb"
44
_ "github.com/netdata/netdata/go/go.d.plugin/modules/mysql"
45
_ "github.com/netdata/netdata/go/go.d.plugin/modules/nginx"
src/go/collectors/go.d.plugin/modules/megacli/charts.go
new
+178
@@ -0,0 +1,178 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package megacli
4
+
5
+import (
6
+ "fmt"
7
+
8
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
9
+)
10
+
11
+const (
12
+ prioAdapterHealthState = module.Priority + iota
13
+
14
+ prioPhysDriveMediaErrorsRate
15
+ prioPhysDrivePredictiveFailuresRate
16
+
17
+ prioBBURelativeCharge
18
+ prioBBURechargeCycles
19
+ prioBBUTemperature
20
+)
21
+
22
+var adapterChartsTmpl = module.Charts{
23
+ adapterHealthStateChartTmpl.Copy(),
24
+}
25
+
26
+var (
27
+ adapterHealthStateChartTmpl = module.Chart{
28
+ ID: "adapter_%s_health_state",
29
+ Title: "Adapter health state",
30
+ Units: "state",
31
+ Fam: "adapter health",
32
+ Ctx: "megacli.adapter_health_state",
33
+ Type: module.Line,
34
+ Priority: prioAdapterHealthState,
35
+ Dims: module.Dims{
36
+ {ID: "adapter_%s_health_state_optimal", Name: "optimal"},
37
+ {ID: "adapter_%s_health_state_degraded", Name: "degraded"},
38
+ {ID: "adapter_%s_health_state_partially_degraded", Name: "partially_degraded"},
39
+ {ID: "adapter_%s_health_state_failed", Name: "failed"},
40
+ },
41
+ }
42
+)
43
+
44
+var physDriveChartsTmpl = module.Charts{
45
+ physDriveMediaErrorsRateChartTmpl.Copy(),
46
+ physDrivePredictiveFailuresRateChartTmpl.Copy(),
47
+}
48
+
49
+var (
50
+ physDriveMediaErrorsRateChartTmpl = module.Chart{
51
+ ID: "phys_drive_%s_media_errors_rate",
52
+ Title: "Physical Drive media errors rate",
53
+ Units: "errors/s",
54
+ Fam: "phys drive errors",
55
+ Ctx: "megacli.phys_drive_media_errors",
56
+ Type: module.Line,
57
+ Priority: prioPhysDriveMediaErrorsRate,
58
+ Dims: module.Dims{
59
+ {ID: "phys_drive_%s_media_error_count", Name: "media_errors"},
60
+ },
61
+ }
62
+ physDrivePredictiveFailuresRateChartTmpl = module.Chart{
63
+ ID: "phys_drive_%s_predictive_failures_rate",
64
+ Title: "Physical Drive predictive failures rate",
65
+ Units: "failures/s",
66
+ Fam: "phys drive errors",
67
+ Ctx: "megacli.phys_drive_predictive_failures",
68
+ Type: module.Line,
69
+ Priority: prioPhysDrivePredictiveFailuresRate,
70
+ Dims: module.Dims{
71
+ {ID: "phys_drive_%s_predictive_failure_count", Name: "predictive_failures"},
72
+ },
73
+ }
74
+)
75
+
76
+var bbuChartsTmpl = module.Charts{
77
+ bbuRelativeChargeChartsTmpl.Copy(),
78
+ bbuRechargeCyclesChartsTmpl.Copy(),
79
+ bbuTemperatureChartsTmpl.Copy(),
80
+}
81
+
82
+var (
83
+ bbuRelativeChargeChartsTmpl = module.Chart{
84
+ ID: "bbu_adapter_%s_relative_charge",
85
+ Title: "BBU relative charge",
86
+ Units: "percentage",
87
+ Fam: "bbu charge",
88
+ Ctx: "megacli.bbu_charge",
89
+ Type: module.Area,
90
+ Priority: prioBBURelativeCharge,
91
+ Dims: module.Dims{
92
+ {ID: "bbu_adapter_%s_relative_state_of_charge", Name: "charge"},
93
+ },
94
+ }
95
+ bbuRechargeCyclesChartsTmpl = module.Chart{
96
+ ID: "bbu_adapter_%s_recharge_cycles",
97
+ Title: "BBU recharge cycles",
98
+ Units: "cycles",
99
+ Fam: "bbu charge",
100
+ Ctx: "megacli.bbu_recharge_cycles",
101
+ Type: module.Line,
102
+ Priority: prioBBURechargeCycles,
103
+ Dims: module.Dims{
104
+ {ID: "bbu_adapter_%s_cycle_count", Name: "recharge"},
105
+ },
106
+ }
107
+ bbuTemperatureChartsTmpl = module.Chart{
108
+ ID: "bbu_adapter_%s_temperature",
109
+ Title: "BBU temperature",
110
+ Units: "Celsius",
111
+ Fam: "bbu temperature",
112
+ Ctx: "megacli.bbu_temperature",
113
+ Type: module.Line,
114
+ Priority: prioBBUTemperature,
115
+ Dims: module.Dims{
116
+ {ID: "bbu_adapter_%s_temperature", Name: "temperature"},
117
+ },
118
+ }
119
+)
120
+
121
+func (m *MegaCli) addAdapterCharts(ad *megaAdapter) {
122
+ charts := adapterChartsTmpl.Copy()
123
+
124
+ for _, chart := range *charts {
125
+ chart.ID = fmt.Sprintf(chart.ID, ad.number)
126
+ chart.Labels = []module.Label{
127
+ {Key: "adapter_number", Value: ad.number},
128
+ }
129
+ for _, dim := range chart.Dims {
130
+ dim.ID = fmt.Sprintf(dim.ID, ad.number)
131
+ }
132
+ }
133
+
134
+ if err := m.Charts().Add(*charts...); err != nil {
135
+ m.Warning(err)
136
+ }
137
+}
138
+
139
+func (m *MegaCli) addPhysDriveCharts(pd *megaPhysDrive) {
140
+ charts := physDriveChartsTmpl.Copy()
141
+
142
+ for _, chart := range *charts {
143
+ chart.ID = fmt.Sprintf(chart.ID, pd.wwn)
144
+ chart.Labels = []module.Label{
145
+ {Key: "adapter_number", Value: pd.adapterNumber},
146
+ {Key: "wwn", Value: pd.wwn},
147
+ {Key: "slot_number", Value: pd.slotNumber},
148
+ {Key: "drive_position", Value: pd.drivePosition},
149
+ {Key: "drive_type", Value: pd.pdType},
150
+ }
151
+ for _, dim := range chart.Dims {
152
+ dim.ID = fmt.Sprintf(dim.ID, pd.wwn)
153
+ }
154
+ }
155
+
156
+ if err := m.Charts().Add(*charts...); err != nil {
157
+ m.Warning(err)
158
+ }
159
+}
160
+
161
+func (m *MegaCli) addBBUCharts(bbu *megaBBU) {
162
+ charts := bbuChartsTmpl.Copy()
163
+
164
+ for _, chart := range *charts {
165
+ chart.ID = fmt.Sprintf(chart.ID, bbu.adapterNumber)
166
+ chart.Labels = []module.Label{
167
+ {Key: "adapter_number", Value: bbu.adapterNumber},
168
+ {Key: "battery_type", Value: bbu.batteryType},
169
+ }
170
+ for _, dim := range chart.Dims {
171
+ dim.ID = fmt.Sprintf(dim.ID, bbu.adapterNumber)
172
+ }
173
+ }
174
+
175
+ if err := m.Charts().Add(*charts...); err != nil {
176
+ m.Warning(err)
177
+ }
178
+}
src/go/collectors/go.d.plugin/modules/megacli/collect.go
new
+46
@@ -0,0 +1,46 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package megacli
4
+
5
+import (
6
+ "strconv"
7
+ "strings"
8
+)
9
+
10
+func (m *MegaCli) collect() (map[string]int64, error) {
11
+ mx := make(map[string]int64)
12
+
13
+ if err := m.collectPhysDrives(mx); err != nil {
14
+ return nil, err
15
+ }
16
+ if err := m.collectBBU(mx); err != nil {
17
+ return nil, err
18
+ }
19
+
20
+ return mx, nil
21
+}
22
+
23
+func writeInt(mx map[string]int64, key, value string) {
24
+ v, err := strconv.ParseInt(value, 10, 64)
25
+ if err != nil {
26
+ return
27
+ }
28
+ mx[key] = v
29
+}
30
+
31
+func getColonSepValue(line string) string {
32
+ i := strings.IndexByte(line, ':')
33
+ if i == -1 {
34
+ return ""
35
+ }
36
+ return strings.TrimSpace(line[i+1:])
37
+}
38
+
39
+func getColonSepNumValue(line string) string {
40
+ v := getColonSepValue(line)
41
+ i := strings.IndexByte(v, ' ')
42
+ if i == -1 {
43
+ return v
44
+ }
45
+ return v[:i]
46
+}
src/go/collectors/go.d.plugin/modules/megacli/collect_bbu.go
new
+105
@@ -0,0 +1,105 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package megacli
4
+
5
+import (
6
+ "bufio"
7
+ "bytes"
8
+ "fmt"
9
+ "strings"
10
+)
11
+
12
+type megaBBU struct {
13
+ adapterNumber string
14
+ batteryType string
15
+ temperature string
16
+ relativeStateOfCharge string
17
+ absoluteStateOfCharge string // apparently can be 0 while relative > 0 (e.g. relative 91%, absolute 0%)
18
+ cycleCount string
19
+}
20
+
21
+func (m *MegaCli) collectBBU(mx map[string]int64) error {
22
+ bs, err := m.exec.bbuInfo()
23
+ if err != nil {
24
+ return err
25
+ }
26
+
27
+ bbus, err := parseBBUInfo(bs)
28
+ if err != nil {
29
+ return err
30
+ }
31
+
32
+ for _, bbu := range bbus {
33
+ if !m.bbu[bbu.adapterNumber] {
34
+ m.bbu[bbu.adapterNumber] = true
35
+ m.addBBUCharts(bbu)
36
+ }
37
+
38
+ px := fmt.Sprintf("bbu_adapter_%s_", bbu.adapterNumber)
39
+
40
+ writeInt(mx, px+"temperature", bbu.temperature)
41
+ writeInt(mx, px+"relative_state_of_charge", bbu.relativeStateOfCharge)
42
+ writeInt(mx, px+"absolute_state_of_charge", bbu.absoluteStateOfCharge)
43
+ writeInt(mx, px+"cycle_count", bbu.cycleCount)
44
+ }
45
+
46
+ return nil
47
+}
48
+
49
+func parseBBUInfo(bs []byte) (map[string]*megaBBU, error) {
50
+ bbus := make(map[string]*megaBBU)
51
+
52
+ var section string
53
+ var bbu *megaBBU
54
+
55
+ sc := bufio.NewScanner(bytes.NewReader(bs))
56
+
57
+ for sc.Scan() {
58
+ line := strings.TrimSpace(sc.Text())
59
+
60
+ switch {
61
+ case strings.HasPrefix(line, "BBU status for Adapter"):
62
+ section = "status"
63
+ ad := getColonSepValue(line)
64
+ if _, ok := bbus[ad]; !ok {
65
+ bbu = &megaBBU{adapterNumber: ad}
66
+ bbus[ad] = bbu
67
+ }
68
+ continue
69
+ case strings.HasPrefix(line, "BBU Capacity Info for Adapter"):
70
+ section = "capacity"
71
+ continue
72
+ case strings.HasPrefix(line, "BBU Firmware Status"),
73
+ strings.HasPrefix(line, "BBU GasGauge Status"),
74
+ strings.HasPrefix(line, "BBU Design Info for Adapter"),
75
+ strings.HasPrefix(line, "BBU Properties for Adapter"):
76
+ section = ""
77
+ continue
78
+ }
79
+
80
+ if bbu == nil {
81
+ continue
82
+ }
83
+
84
+ switch section {
85
+ case "status":
86
+ switch {
87
+ case strings.HasPrefix(line, "BatteryType:"):
88
+ bbu.batteryType = getColonSepValue(line)
89
+ case strings.HasPrefix(line, "Temperature:"):
90
+ bbu.temperature = getColonSepNumValue(line)
91
+ }
92
+ case "capacity":
93
+ switch {
94
+ case strings.HasPrefix(line, "Relative State of Charge:"):
95
+ bbu.relativeStateOfCharge = getColonSepNumValue(line)
96
+ case strings.HasPrefix(line, "Absolute State of charge:"):
97
+ bbu.absoluteStateOfCharge = getColonSepNumValue(line)
98
+ case strings.HasPrefix(line, "Cycle Count:"):
99
+ bbu.cycleCount = getColonSepNumValue(line)
100
+ }
101
+ }
102
+ }
103
+
104
+ return bbus, nil
105
+}
src/go/collectors/go.d.plugin/modules/megacli/collect_phys_drives.go
new
+120
@@ -0,0 +1,120 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package megacli
4
+
5
+import (
6
+ "bufio"
7
+ "bytes"
8
+ "fmt"
9
+ "strings"
10
+)
11
+
12
+type (
13
+ megaAdapter struct {
14
+ number string
15
+ name string
16
+ state string
17
+ physDrives map[string]*megaPhysDrive
18
+ }
19
+ megaPhysDrive struct {
20
+ adapterNumber string
21
+ number string
22
+ wwn string
23
+ slotNumber string
24
+ drivePosition string
25
+ pdType string
26
+ mediaErrorCount string
27
+ predictiveFailureCount string
28
+ }
29
+)
30
+
31
+var adapterStates = []string{
32
+ "optimal",
33
+ "degraded",
34
+ "partially_degraded",
35
+ "failed",
36
+}
37
+
38
+func (m *MegaCli) collectPhysDrives(mx map[string]int64) error {
39
+ bs, err := m.exec.physDrivesInfo()
40
+ if err != nil {
41
+ return err
42
+ }
43
+
44
+ adapters, err := parsePhysDrivesInfo(bs)
45
+ if err != nil {
46
+ return err
47
+ }
48
+
49
+ for _, ad := range adapters {
50
+ if !m.adapters[ad.number] {
51
+ m.adapters[ad.number] = true
52
+ m.addAdapterCharts(ad)
53
+ }
54
+
55
+ px := fmt.Sprintf("adapter_%s_health_state_", ad.number)
56
+ for _, st := range adapterStates {
57
+ mx[px+st] = 0
58
+ }
59
+ st := strings.ReplaceAll(strings.ToLower(ad.state), " ", "_")
60
+ mx[px+st] = 1
61
+
62
+ for _, pd := range ad.physDrives {
63
+ if !m.adapters[pd.wwn] {
64
+ m.adapters[pd.wwn] = true
65
+ m.addPhysDriveCharts(pd)
66
+ }
67
+
68
+ px := fmt.Sprintf("phys_drive_%s_", pd.wwn)
69
+
70
+ writeInt(mx, px+"media_error_count", pd.mediaErrorCount)
71
+ writeInt(mx, px+"predictive_failure_count", pd.predictiveFailureCount)
72
+ }
73
+ }
74
+
75
+ return nil
76
+}
77
+
78
+func parsePhysDrivesInfo(bs []byte) (map[string]*megaAdapter, error) {
79
+ adapters := make(map[string]*megaAdapter)
80
+
81
+ var ad *megaAdapter
82
+ var pd *megaPhysDrive
83
+
84
+ sc := bufio.NewScanner(bytes.NewReader(bs))
85
+
86
+ for sc.Scan() {
87
+ line := strings.TrimSpace(sc.Text())
88
+
89
+ switch {
90
+ case strings.HasPrefix(line, "Adapter #"):
91
+ idx := strings.TrimPrefix(line, "Adapter #")
92
+ ad = &megaAdapter{number: idx, physDrives: make(map[string]*megaPhysDrive)}
93
+ adapters[idx] = ad
94
+ case strings.HasPrefix(line, "Name") && ad != nil:
95
+ ad.name = getColonSepValue(line)
96
+ case strings.HasPrefix(line, "State") && ad != nil:
97
+ ad.state = getColonSepValue(line)
98
+ case strings.HasPrefix(line, "PD:") && ad != nil:
99
+ if parts := strings.Fields(line); len(parts) == 3 {
100
+ idx := parts[1]
101
+ pd = &megaPhysDrive{number: idx, adapterNumber: ad.number}
102
+ ad.physDrives[idx] = pd
103
+ }
104
+ case strings.HasPrefix(line, "Slot Number:") && pd != nil:
105
+ pd.slotNumber = getColonSepValue(line)
106
+ case strings.HasPrefix(line, "Drive's position:") && pd != nil:
107
+ pd.drivePosition = getColonSepValue(line)
108
+ case strings.HasPrefix(line, "WWN:") && pd != nil:
109
+ pd.wwn = getColonSepValue(line)
110
+ case strings.HasPrefix(line, "PD Type:") && pd != nil:
111
+ pd.pdType = getColonSepValue(line)
112
+ case strings.HasPrefix(line, "Media Error Count:") && pd != nil:
113
+ pd.mediaErrorCount = getColonSepNumValue(line)
114
+ case strings.HasPrefix(line, "Predictive Failure Count:") && pd != nil:
115
+ pd.predictiveFailureCount = getColonSepNumValue(line)
116
+ }
117
+ }
118
+
119
+ return adapters, nil
120
+}
src/go/collectors/go.d.plugin/modules/megacli/config_schema.json
new
+35
@@ -0,0 +1,35 @@
1
+{
2
+ "jsonSchema": {
3
+ "$schema": "http://json-schema.org/draft-07/schema#",
4
+ "title": "MegaCli collector configuration.",
5
+ "type": "object",
6
+ "properties": {
7
+ "update_every": {
8
+ "title": "Update every",
9
+ "description": "Data collection interval, measured in seconds.",
10
+ "type": "integer",
11
+ "minimum": 1,
12
+ "default": 10
13
+ },
14
+ "timeout": {
15
+ "title": "Timeout",
16
+ "description": "Timeout for executing the binary, specified in seconds.",
17
+ "type": "number",
18
+ "minimum": 0.5,
19
+ "default": 2
20
+ }
21
+ },
22
+ "additionalProperties": false,
23
+ "patternProperties": {
24
+ "^name$": {}
25
+ }
26
+ },
27
+ "uiSchema": {
28
+ "uiOptions": {
29
+ "fullPage": true
30
+ },
31
+ "timeout": {
32
+ "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
33
+ }
34
+ }
35
+}
src/go/collectors/go.d.plugin/modules/megacli/exec.go
new
+50
@@ -0,0 +1,50 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package megacli
4
+
5
+import (
6
+ "context"
7
+ "fmt"
8
+ "os/exec"
9
+ "time"
10
+
11
+ "github.com/netdata/netdata/go/go.d.plugin/logger"
12
+)
13
+
14
+func newMegaCliExec(ndsudoPath string, timeout time.Duration, log *logger.Logger) *megaCliExec {
15
+ return &megaCliExec{
16
+ Logger: log,
17
+ ndsudoPath: ndsudoPath,
18
+ timeout: timeout,
19
+ }
20
+}
21
+
22
+type megaCliExec struct {
23
+ *logger.Logger
24
+
25
+ ndsudoPath string
26
+ timeout time.Duration
27
+}
28
+
29
+func (e *megaCliExec) physDrivesInfo() ([]byte, error) {
30
+ return e.execute("megacli-disk-info")
31
+}
32
+
33
+func (e *megaCliExec) bbuInfo() ([]byte, error) {
34
+ return e.execute("megacli-battery-info")
35
+}
36
+
37
+func (e *megaCliExec) execute(args ...string) ([]byte, error) {
38
+ ctx, cancel := context.WithTimeout(context.Background(), e.timeout)
39
+ defer cancel()
40
+
41
+ cmd := exec.CommandContext(ctx, e.ndsudoPath, args...)
42
+ e.Debugf("executing '%s'", cmd)
43
+
44
+ bs, err := cmd.Output()
45
+ if err != nil {
46
+ return nil, fmt.Errorf("error on '%s': %v", cmd, err)
47
+ }
48
+
49
+ return bs, nil
50
+}
src/go/collectors/go.d.plugin/modules/megacli/init.go
new
+23
@@ -0,0 +1,23 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package megacli
4
+
5
+import (
6
+ "fmt"
7
+ "os"
8
+ "path/filepath"
9
+
10
+ "github.com/netdata/netdata/go/go.d.plugin/agent/executable"
11
+)
12
+
13
+func (m *MegaCli) initMegaCliExec() (megaCli, error) {
14
+ ndsudoPath := filepath.Join(executable.Directory, "ndsudo")
15
+
16
+ if _, err := os.Stat(ndsudoPath); err != nil {
17
+ return nil, fmt.Errorf("ndsudo executable not found: %v", err)
18
+ }
19
+
20
+ megaExec := newMegaCliExec(ndsudoPath, m.Timeout.Duration(), m.Logger)
21
+
22
+ return megaExec, nil
23
+}
src/go/collectors/go.d.plugin/modules/megacli/megacli.go
new
+109
@@ -0,0 +1,109 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package megacli
4
+
5
+import (
6
+ _ "embed"
7
+ "errors"
8
+ "time"
9
+
10
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
11
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/web"
12
+)
13
+
14
+//go:embed "config_schema.json"
15
+var configSchema string
16
+
17
+func init() {
18
+ module.Register("megacli", module.Creator{
19
+ JobConfigSchema: configSchema,
20
+ Defaults: module.Defaults{
21
+ UpdateEvery: 10,
22
+ },
23
+ Create: func() module.Module { return New() },
24
+ })
25
+}
26
+
27
+func New() *MegaCli {
28
+ return &MegaCli{
29
+ Config: Config{
30
+ Timeout: web.Duration(time.Second * 2),
31
+ },
32
+ charts: &module.Charts{},
33
+ adapters: make(map[string]bool),
34
+ drives: make(map[string]bool),
35
+ bbu: make(map[string]bool),
36
+ }
37
+}
38
+
39
+type Config struct {
40
+ UpdateEvery int `yaml:"update_every" json:"update_every"`
41
+ Timeout web.Duration `yaml:"timeout" json:"timeout"`
42
+}
43
+
44
+type (
45
+ MegaCli struct {
46
+ module.Base
47
+ Config `yaml:",inline" json:""`
48
+
49
+ charts *module.Charts
50
+
51
+ exec megaCli
52
+
53
+ adapters map[string]bool
54
+ drives map[string]bool
55
+ bbu map[string]bool
56
+ }
57
+ megaCli interface {
58
+ physDrivesInfo() ([]byte, error)
59
+ bbuInfo() ([]byte, error)
60
+ }
61
+)
62
+
63
+func (m *MegaCli) Configuration() any {
64
+ return m.Config
65
+}
66
+
67
+func (m *MegaCli) Init() error {
68
+ lvmExec, err := m.initMegaCliExec()
69
+ if err != nil {
70
+ m.Errorf("megacli exec initialization: %v", err)
71
+ return err
72
+ }
73
+ m.exec = lvmExec
74
+
75
+ return nil
76
+}
77
+
78
+func (m *MegaCli) Check() error {
79
+ mx, err := m.collect()
80
+ if err != nil {
81
+ m.Error(err)
82
+ return err
83
+ }
84
+
85
+ if len(mx) == 0 {
86
+ return errors.New("no metrics collected")
87
+ }
88
+
89
+ return nil
90
+}
91
+
92
+func (m *MegaCli) Charts() *module.Charts {
93
+ return m.charts
94
+}
95
+
96
+func (m *MegaCli) Collect() map[string]int64 {
97
+ mx, err := m.collect()
98
+ if err != nil {
99
+ m.Error(err)
100
+ }
101
+
102
+ if len(mx) == 0 {
103
+ return nil
104
+ }
105
+
106
+ return mx
107
+}
108
+
109
+func (m *MegaCli) Cleanup() {}
src/go/collectors/go.d.plugin/modules/megacli/megacli_test.go
new
+296
@@ -0,0 +1,296 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package megacli
4
+
5
+import (
6
+ "errors"
7
+ "os"
8
+ "testing"
9
+
10
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
11
+
12
+ "github.com/stretchr/testify/assert"
13
+ "github.com/stretchr/testify/require"
14
+)
15
+
16
+var (
17
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
18
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
19
+
20
+ dataBBUInfoOld, _ = os.ReadFile("testdata/mega-bbu-info-old.txt")
21
+ dataBBUInfoRecent, _ = os.ReadFile("testdata/mega-bbu-info-recent.txt")
22
+ dataPhysDrivesInfo, _ = os.ReadFile("testdata/mega-phys-drives-info.txt")
23
+)
24
+
25
+func Test_testDataIsValid(t *testing.T) {
26
+ for name, data := range map[string][]byte{
27
+ "dataConfigJSON": dataConfigJSON,
28
+ "dataConfigYAML": dataConfigYAML,
29
+
30
+ "dataBBUInfoOld": dataBBUInfoOld,
31
+ "dataBBUInfoRecent": dataBBUInfoRecent,
32
+ "dataPhysDrivesInfo": dataPhysDrivesInfo,
33
+ } {
34
+ require.NotNil(t, data, name)
35
+ }
36
+}
37
+
38
+func TestMegaCli_ConfigurationSerialize(t *testing.T) {
39
+ module.TestConfigurationSerialize(t, &MegaCli{}, dataConfigJSON, dataConfigYAML)
40
+}
41
+
42
+func TestMegaCli_Init(t *testing.T) {
43
+ tests := map[string]struct {
44
+ config Config
45
+ wantFail bool
46
+ }{
47
+ "fails if 'ndsudo' not found": {
48
+ wantFail: true,
49
+ config: New().Config,
50
+ },
51
+ }
52
+
53
+ for name, test := range tests {
54
+ t.Run(name, func(t *testing.T) {
55
+ mega := New()
56
+
57
+ if test.wantFail {
58
+ assert.Error(t, mega.Init())
59
+ } else {
60
+ assert.NoError(t, mega.Init())
61
+ }
62
+ })
63
+ }
64
+}
65
+
66
+func TestMegaCli_Cleanup(t *testing.T) {
67
+ tests := map[string]struct {
68
+ prepare func() *MegaCli
69
+ }{
70
+ "not initialized exec": {
71
+ prepare: func() *MegaCli {
72
+ return New()
73
+ },
74
+ },
75
+ "after check": {
76
+ prepare: func() *MegaCli {
77
+ mega := New()
78
+ mega.exec = prepareMockOK()
79
+ _ = mega.Check()
80
+ return mega
81
+ },
82
+ },
83
+ "after collect": {
84
+ prepare: func() *MegaCli {
85
+ mega := New()
86
+ mega.exec = prepareMockOK()
87
+ _ = mega.Collect()
88
+ return mega
89
+ },
90
+ },
91
+ }
92
+
93
+ for name, test := range tests {
94
+ t.Run(name, func(t *testing.T) {
95
+ mega := test.prepare()
96
+
97
+ assert.NotPanics(t, mega.Cleanup)
98
+ })
99
+ }
100
+}
101
+
102
+func TestMegaCli_Charts(t *testing.T) {
103
+ assert.NotNil(t, New().Charts())
104
+}
105
+
106
+func TestMegaCli_Check(t *testing.T) {
107
+ tests := map[string]struct {
108
+ prepareMock func() *mockMegaCliExec
109
+ wantFail bool
110
+ }{
111
+ "success case": {
112
+ wantFail: false,
113
+ prepareMock: prepareMockOK,
114
+ },
115
+ "success case old bbu": {
116
+ wantFail: false,
117
+ prepareMock: prepareMockOldBbuOK,
118
+ },
119
+ "err on exec": {
120
+ wantFail: true,
121
+ prepareMock: prepareMockErr,
122
+ },
123
+ "unexpected response": {
124
+ wantFail: true,
125
+ prepareMock: prepareMockUnexpectedResponse,
126
+ },
127
+ "empty response": {
128
+ wantFail: true,
129
+ prepareMock: prepareMockEmptyResponse,
130
+ },
131
+ }
132
+
133
+ for name, test := range tests {
134
+ t.Run(name, func(t *testing.T) {
135
+ mega := New()
136
+ mock := test.prepareMock()
137
+ mega.exec = mock
138
+
139
+ if test.wantFail {
140
+ assert.Error(t, mega.Check())
141
+ } else {
142
+ assert.NoError(t, mega.Check())
143
+ }
144
+ })
145
+ }
146
+}
147
+
148
+func TestMegaCli_Collect(t *testing.T) {
149
+ tests := map[string]struct {
150
+ prepareMock func() *mockMegaCliExec
151
+ wantMetrics map[string]int64
152
+ wantCharts int
153
+ }{
154
+ "success case": {
155
+ prepareMock: prepareMockOK,
156
+ wantCharts: len(adapterChartsTmpl)*1 + len(physDriveChartsTmpl)*8 + len(bbuChartsTmpl)*1,
157
+ wantMetrics: map[string]int64{
158
+ "adapter_0_health_state_degraded": 0,
159
+ "adapter_0_health_state_failed": 0,
160
+ "adapter_0_health_state_optimal": 1,
161
+ "adapter_0_health_state_partially_degraded": 0,
162
+ "bbu_adapter_0_absolute_state_of_charge": 63,
163
+ "bbu_adapter_0_cycle_count": 4,
164
+ "bbu_adapter_0_relative_state_of_charge": 71,
165
+ "bbu_adapter_0_temperature": 33,
166
+ "phys_drive_5002538c00019b96_media_error_count": 0,
167
+ "phys_drive_5002538c00019b96_predictive_failure_count": 0,
168
+ "phys_drive_5002538c4002da83_media_error_count": 0,
169
+ "phys_drive_5002538c4002da83_predictive_failure_count": 0,
170
+ "phys_drive_5002538c4002dade_media_error_count": 0,
171
+ "phys_drive_5002538c4002dade_predictive_failure_count": 0,
172
+ "phys_drive_5002538c4002e6e9_media_error_count": 0,
173
+ "phys_drive_5002538c4002e6e9_predictive_failure_count": 0,
174
+ "phys_drive_5002538c4002e707_media_error_count": 0,
175
+ "phys_drive_5002538c4002e707_predictive_failure_count": 0,
176
+ "phys_drive_5002538c4002e70f_media_error_count": 0,
177
+ "phys_drive_5002538c4002e70f_predictive_failure_count": 0,
178
+ "phys_drive_5002538c4002e712_media_error_count": 0,
179
+ "phys_drive_5002538c4002e712_predictive_failure_count": 0,
180
+ "phys_drive_5002538c4002e713_media_error_count": 0,
181
+ "phys_drive_5002538c4002e713_predictive_failure_count": 0,
182
+ },
183
+ },
184
+ "success case old bbu": {
185
+ prepareMock: prepareMockOldBbuOK,
186
+ wantCharts: len(adapterChartsTmpl)*1 + len(physDriveChartsTmpl)*8 + len(bbuChartsTmpl)*1,
187
+ wantMetrics: map[string]int64{
188
+ "adapter_0_health_state_degraded": 0,
189
+ "adapter_0_health_state_failed": 0,
190
+ "adapter_0_health_state_optimal": 1,
191
+ "adapter_0_health_state_partially_degraded": 0,
192
+ "bbu_adapter_0_absolute_state_of_charge": 83,
193
+ "bbu_adapter_0_cycle_count": 61,
194
+ "bbu_adapter_0_relative_state_of_charge": 100,
195
+ "bbu_adapter_0_temperature": 31,
196
+ "phys_drive_5002538c00019b96_media_error_count": 0,
197
+ "phys_drive_5002538c00019b96_predictive_failure_count": 0,
198
+ "phys_drive_5002538c4002da83_media_error_count": 0,
199
+ "phys_drive_5002538c4002da83_predictive_failure_count": 0,
200
+ "phys_drive_5002538c4002dade_media_error_count": 0,
201
+ "phys_drive_5002538c4002dade_predictive_failure_count": 0,
202
+ "phys_drive_5002538c4002e6e9_media_error_count": 0,
203
+ "phys_drive_5002538c4002e6e9_predictive_failure_count": 0,
204
+ "phys_drive_5002538c4002e707_media_error_count": 0,
205
+ "phys_drive_5002538c4002e707_predictive_failure_count": 0,
206
+ "phys_drive_5002538c4002e70f_media_error_count": 0,
207
+ "phys_drive_5002538c4002e70f_predictive_failure_count": 0,
208
+ "phys_drive_5002538c4002e712_media_error_count": 0,
209
+ "phys_drive_5002538c4002e712_predictive_failure_count": 0,
210
+ "phys_drive_5002538c4002e713_media_error_count": 0,
211
+ "phys_drive_5002538c4002e713_predictive_failure_count": 0,
212
+ },
213
+ },
214
+ "err on exec": {
215
+ prepareMock: prepareMockErr,
216
+ wantMetrics: nil,
217
+ },
218
+ "unexpected response": {
219
+ prepareMock: prepareMockUnexpectedResponse,
220
+ wantMetrics: nil,
221
+ },
222
+ "empty response": {
223
+ prepareMock: prepareMockEmptyResponse,
224
+ wantMetrics: nil,
225
+ },
226
+ }
227
+
228
+ for name, test := range tests {
229
+ t.Run(name, func(t *testing.T) {
230
+ mega := New()
231
+ mock := test.prepareMock()
232
+ mega.exec = mock
233
+
234
+ mx := mega.Collect()
235
+
236
+ assert.Equal(t, test.wantMetrics, mx)
237
+ assert.Len(t, *mega.Charts(), test.wantCharts)
238
+ })
239
+ }
240
+}
241
+
242
+func prepareMockOK() *mockMegaCliExec {
243
+ return &mockMegaCliExec{
244
+ physDrivesInfoData: dataPhysDrivesInfo,
245
+ bbuInfoData: dataBBUInfoRecent,
246
+ }
247
+}
248
+
249
+func prepareMockOldBbuOK() *mockMegaCliExec {
250
+ return &mockMegaCliExec{
251
+ physDrivesInfoData: dataPhysDrivesInfo,
252
+ bbuInfoData: dataBBUInfoOld,
253
+ }
254
+}
255
+
256
+func prepareMockErr() *mockMegaCliExec {
257
+ return &mockMegaCliExec{
258
+ errOnInfo: true,
259
+ }
260
+}
261
+
262
+func prepareMockUnexpectedResponse() *mockMegaCliExec {
263
+ resp := []byte(`
264
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
265
+Nulla malesuada erat id magna mattis, eu viverra tellus rhoncus.
266
+Fusce et felis pulvinar, posuere sem non, porttitor eros.
267
+`)
268
+ return &mockMegaCliExec{
269
+ physDrivesInfoData: resp,
270
+ bbuInfoData: resp,
271
+ }
272
+}
273
+
274
+func prepareMockEmptyResponse() *mockMegaCliExec {
275
+ return &mockMegaCliExec{}
276
+}
277
+
278
+type mockMegaCliExec struct {
279
+ errOnInfo bool
280
+ physDrivesInfoData []byte
281
+ bbuInfoData []byte
282
+}
283
+
284
+func (m *mockMegaCliExec) physDrivesInfo() ([]byte, error) {
285
+ if m.errOnInfo {
286
+ return nil, errors.New("mock.physDrivesInfo() error")
287
+ }
288
+ return m.physDrivesInfoData, nil
289
+}
290
+
291
+func (m *mockMegaCliExec) bbuInfo() ([]byte, error) {
292
+ if m.errOnInfo {
293
+ return nil, errors.New("mock.bbuInfo() error")
294
+ }
295
+ return m.bbuInfoData, nil
296
+}
src/go/collectors/go.d.plugin/modules/megacli/metadata.yaml
new
+157
@@ -0,0 +1,157 @@
1
+plugin_name: go.d.plugin
2
+modules:
3
+ - meta:
4
+ id: collector-go.d.plugin-megacli
5
+ plugin_name: go.d.plugin
6
+ module_name: megacli
7
+ monitored_instance:
8
+ name: MegaCli Hardware Raid
9
+ link: "https://wikitech.wikimedia.org/wiki/MegaCli"
10
+ icon_filename: "hard-drive.svg"
11
+ categories:
12
+ - data-collection.storage-mount-points-and-filesystems
13
+ keywords:
14
+ - storage
15
+ - raid-controller
16
+ - manage-disks
17
+ related_resources:
18
+ integrations:
19
+ list: []
20
+ info_provided_to_referring_integrations:
21
+ description: ""
22
+ most_popular: false
23
+ overview:
24
+ data_collection:
25
+ metrics_description: >
26
+ Monitors the health of MegaCLI Hardware RAID by tracking the status of RAID adapters, physical drives, and backup batteries in your storage system.
27
+ It relies on the `megacli` CLI tool but avoids directly executing the binary.
28
+ Instead, it utilizes `ndsudo`, a Netdata helper specifically designed to run privileged commands securely within the Netdata environment.
29
+ This approach eliminates the need to use `sudo`, improving security and potentially simplifying permission management.
30
+
31
+ Executed commands:
32
+ - `megacli -LDPDInfo -aAll -NoLog`
33
+ - `megacli -AdpBbuCmd -aAll -NoLog`
34
+ method_description: ""
35
+ supported_platforms:
36
+ include: []
37
+ exclude: []
38
+ multi_instance: false
39
+ additional_permissions:
40
+ description: ""
41
+ default_behavior:
42
+ auto_detection:
43
+ description: ""
44
+ limits:
45
+ description: ""
46
+ performance_impact:
47
+ description: ""
48
+ setup:
49
+ prerequisites:
50
+ list: []
51
+ configuration:
52
+ file:
53
+ name: go.d/megacli.conf
54
+ options:
55
+ description: |
56
+ The following options can be defined globally: update_every.
57
+ folding:
58
+ title: Config options
59
+ enabled: true
60
+ list:
61
+ - name: update_every
62
+ description: Data collection frequency.
63
+ default_value: 10
64
+ required: false
65
+ - name: timeout
66
+ description: lvs binary execution timeout.
67
+ default_value: 2
68
+ required: false
69
+ examples:
70
+ folding:
71
+ title: Config
72
+ enabled: true
73
+ list:
74
+ - name: Custom update_every
75
+ description: Allows you to override the default data collection interval.
76
+ config: |
77
+ jobs:
78
+ - name: megacli
79
+ update_every: 5 # Collect MegaCli Hardware RAID statistics every 5 seconds
80
+ troubleshooting:
81
+ problems:
82
+ list: []
83
+ alerts: []
84
+ metrics:
85
+ folding:
86
+ title: Metrics
87
+ enabled: false
88
+ description: ""
89
+ availability: []
90
+ scopes:
91
+ - name: adapter
92
+ description: These metrics refer to the MegaCLI Adapter.
93
+ labels:
94
+ - name: adapter_number
95
+ description: Adapter number
96
+ metrics:
97
+ - name: megacli.adapter_health_state
98
+ description: Adapter health state
99
+ unit: state
100
+ chart_type: line
101
+ dimensions:
102
+ - name: optimal
103
+ - name: degraded
104
+ - name: partially_degraded
105
+ - name: failed
106
+ - name: physical drive
107
+ description: These metrics refer to the MegaCLI Physical Drive.
108
+ labels:
109
+ - name: adapter_number
110
+ description: Adapter number
111
+ - name: wwn
112
+ description: World Wide Name
113
+ - name: slot_number
114
+ description: Slot number
115
+ - name: drive_position
116
+ description: "Position (e.g. DiskGroup: 0, Span: 0, Arm: 2)"
117
+ - name: drive_type
118
+ description: Type (e.g. SATA)
119
+ metrics:
120
+ - name: megacli.phys_drive_media_errors_rate
121
+ description: Physical Drive media errors rate
122
+ unit: errors/s
123
+ chart_type: line
124
+ dimensions:
125
+ - name: media_errors
126
+ - name: megacli.phys_drive_predictive_failures_rate
127
+ description: Physical Drive predictive failures rate
128
+ unit: failures/s
129
+ chart_type: line
130
+ dimensions:
131
+ - name: predictive_failures
132
+ - name: backup battery unit
133
+ description: These metrics refer to the MegaCLI Backup Battery Unit.
134
+ labels:
135
+ - name: adapter_number
136
+ description: Adapter number
137
+ - name: battery_type
138
+ description: Battery type (e.g. BBU)
139
+ metrics:
140
+ - name: megacli.bbu_relative_charge
141
+ description: BBU relative charge
142
+ unit: percentage
143
+ chart_type: area
144
+ dimensions:
145
+ - name: charge
146
+ - name: megacli.bbu_recharge_cycles
147
+ description: BBU relative charge
148
+ unit: cycles
149
+ chart_type: line
150
+ dimensions:
151
+ - name: recharge
152
+ - name: megacli.bbu_temperature
153
+ description: BBU bbu_temperature
154
+ unit: Celsius
155
+ chart_type: line
156
+ dimensions:
157
+ - name: temperature
src/go/collectors/go.d.plugin/modules/megacli/testdata/config.json
new
+4
@@ -0,0 +1,4 @@
1
+{
2
+ "update_every": 123,
3
+ "timeout": 123.123
4
+}
src/go/collectors/go.d.plugin/modules/megacli/testdata/config.yaml
new
+2
@@ -0,0 +1,2 @@
1
+update_every: 123
2
+timeout: 123.123
src/go/collectors/go.d.plugin/modules/megacli/testdata/mega-bbu-info-old.txt
new
+84
@@ -0,0 +1,84 @@
1
+BBU status for Adapter: 0
2
+
3
+BatteryType: BBU
4
+Voltage: 4073 mV
5
+Current: 0 mA
6
+Temperature: 31 C
7
+Battery State: Optimal
8
+BBU Firmware Status:
9
+
10
+ Charging Status : None
11
+ Voltage : OK
12
+ Temperature : OK
13
+ Learn Cycle Requested : No
14
+ Learn Cycle Active : No
15
+ Learn Cycle Status : OK
16
+ Learn Cycle Timeout : No
17
+ I2c Errors Detected : No
18
+ Battery Pack Missing : No
19
+ Battery Replacement required : No
20
+ Remaining Capacity Low : No
21
+ Periodic Learn Required : No
22
+ Transparent Learn : No
23
+ No space to cache offload : No
24
+ Pack is about to fail & should be replaced : No
25
+ Cache Offload premium feature required : No
26
+ Module microcode update required : No
27
+
28
+
29
+GasGuageStatus:
30
+ Fully Discharged : No
31
+ Fully Charged : Yes
32
+ Discharging : Yes
33
+ Initialized : Yes
34
+ Remaining Time Alarm : No
35
+ Discharge Terminated : No
36
+ Over Temperature : No
37
+ Charging Terminated : Yes
38
+ Over Charged : No
39
+Relative State of Charge: 100 %
40
+Charger Status: Complete
41
+Remaining Capacity: 1477 mAh
42
+Full Charge Capacity: 1477 mAh
43
+isSOHGood: Yes
44
+ Battery backup charge time : 0 hours
45
+
46
+BBU Capacity Info for Adapter: 0
47
+
48
+ Relative State of Charge: 100 %
49
+ Absolute State of charge: 83 %
50
+ Remaining Capacity: 1477 mAh
51
+ Full Charge Capacity: 1477 mAh
52
+ Run time to empty: Battery is not being charged.
53
+ Average time to empty: Battery is not being charged.
54
+ Estimated Time to full recharge: Battery is not being charged.
55
+ Cycle Count: 61
56
+Max Error = 2 %
57
+Remaining Capacity Alarm = 180 mAh
58
+Remining Time Alarm = 10 Min
59
+
60
+BBU Design Info for Adapter: 0
61
+
62
+ Date of Manufacture: 07/08, 2010
63
+ Design Capacity: 1800 mAh
64
+ Design Voltage: 3700 mV
65
+ Specification Info: 49
66
+ Serial Number: 4069
67
+ Pack Stat Configuration: 0x0014
68
+ Manufacture Name: SMP-PA1.9
69
+ Firmware Version : �
70
+ Device Name: DLFR463
71
+ Device Chemistry: LION
72
+ Battery FRU: N/A
73
+Module Version = �
74
+ Transparent Learn = 0
75
+ App Data = 0
76
+
77
+BBU Properties for Adapter: 0
78
+
79
+ Auto Learn Period: 90 Days
80
+ Next Learn time: Fri Jan 28 13:07:56 2022
81
+ Learn Delay Interval:0 Hours
82
+ Auto-Learn Mode: Enabled
83
+
84
+Exit Code: 0x00
src/go/collectors/go.d.plugin/modules/megacli/testdata/mega-bbu-info-recent.txt
new
+74
@@ -0,0 +1,74 @@
1
+BBU status for Adapter: 0
2
+
3
+BatteryType: iBBU08
4
+Voltage: 3922 mV
5
+Current: 0 mA
6
+Temperature: 33 C
7
+Battery State: Optimal
8
+Design Mode : 48+ Hrs retention with a non-transparent learn cycle and balanced service life.
9
+
10
+BBU Firmware Status:
11
+
12
+ Charging Status : None
13
+ Voltage : OK
14
+ Temperature : OK
15
+ Learn Cycle Requested : No
16
+ Learn Cycle Active : No
17
+ Learn Cycle Status : OK
18
+ Learn Cycle Timeout : No
19
+ I2c Errors Detected : No
20
+ Battery Pack Missing : No
21
+ Battery Replacement required : No
22
+ Remaining Capacity Low : No
23
+ Periodic Learn Required : No
24
+ Transparent Learn : No
25
+ No space to cache offload : No
26
+ Pack is about to fail & should be replaced : No
27
+ Cache Offload premium feature required : No
28
+ Module microcode update required : No
29
+
30
+BBU GasGauge Status: 0x0100
31
+ Relative State of Charge: 71 %
32
+ Charger System State: 1
33
+ Charger System Ctrl: 0
34
+ Charging current: 0 mA
35
+ Absolute state of charge: 63 %
36
+ Max Error: 0 %
37
+ Battery backup charge time : 48 hours +
38
+
39
+BBU Capacity Info for Adapter: 0
40
+
41
+ Relative State of Charge: 71 %
42
+ Absolute State of charge: 63 %
43
+ Remaining Capacity: 969 mAh
44
+ Full Charge Capacity: 1365 mAh
45
+ Run time to empty: Battery is not being charged.
46
+ Average time to empty: 1 Hour, 56 Min.
47
+ Estimated Time to full recharge: Battery is not being charged.
48
+ Cycle Count: 4
49
+
50
+BBU Design Info for Adapter: 0
51
+
52
+ Date of Manufacture: 03/18, 2011
53
+ Design Capacity: 1530 mAh
54
+ Design Voltage: 4100 mV
55
+ Specification Info: 0
56
+ Serial Number: 5164
57
+ Pack Stat Configuration: 0x0000
58
+ Manufacture Name: LS36681
59
+ Firmware Version :
60
+ Device Name: bq27541
61
+ Device Chemistry: LPMR
62
+ Battery FRU: N/A
63
+ Transparent Learn = 0
64
+ App Data = 0
65
+
66
+BBU Properties for Adapter: 0
67
+
68
+ Auto Learn Period: 28 Days
69
+ Next Learn time: Thu Dec 21 18:32:56 2023
70
+ Learn Delay Interval:0 Hours
71
+ Auto-Learn Mode: Enabled
72
+ BBU Mode = 4
73
+
74
+Exit Code: 0x00
src/go/collectors/go.d.plugin/modules/megacli/testdata/mega-phys-drives-info.txt
new
+433
@@ -0,0 +1,433 @@
1
+Adapter #0
2
+
3
+Number of Virtual Disks: 1
4
+Virtual Drive: 0 (Target Id: 0)
5
+Name :Virtual Disk 0
6
+RAID Level : Primary-1, Secondary-0, RAID Level Qualifier-0
7
+Size : 3.491 TB
8
+Sector Size : 512
9
+Is VD emulated : No
10
+Mirror Data : 3.491 TB
11
+State : Optimal
12
+Strip Size : 64 KB
13
+Number Of Drives : 8
14
+Span Depth : 1
15
+Default Cache Policy: WriteBack, ReadAhead, Direct, No Write Cache if Bad BBU
16
+Current Cache Policy: WriteBack, ReadAhead, Direct, No Write Cache if Bad BBU
17
+Default Access Policy: Read/Write
18
+Current Access Policy: Read/Write
19
+Disk Cache Policy : Disk's Default
20
+Encryption Type : None
21
+Default Power Savings Policy: Controller Defined
22
+Current Power Savings Policy: None
23
+Can spin up in 1 minute: No
24
+LD has drives that support T10 power conditions: No
25
+LD's IO profile supports MAX power savings with cached writes: No
26
+Bad Blocks Exist: No
27
+PI type: No PI
28
+
29
+Is VD Cached: No
30
+Number of Spans: 1
31
+Span: 0 - Number of PDs: 8
32
+
33
+PD: 0 Information
34
+Enclosure Device ID: 32
35
+Slot Number: 0
36
+Drive's position: DiskGroup: 0, Span: 0, Arm: 0
37
+Enclosure position: 1
38
+Device Id: 0
39
+WWN: 5002538c4002e713
40
+Sequence Number: 2
41
+Media Error Count: 0
42
+Other Error Count: 0
43
+Predictive Failure Count: 0
44
+Last Predictive Failure Event Seq Number: 0
45
+PD Type: SATA
46
+
47
+Raw Size: 894.252 GB [0x6fc81ab0 Sectors]
48
+Non Coerced Size: 893.752 GB [0x6fb81ab0 Sectors]
49
+Coerced Size: 893.75 GB [0x6fb80000 Sectors]
50
+Sector Size: 512
51
+Logical Sector Size: 512
52
+Physical Sector Size: 512
53
+Firmware state: Online, Spun Up
54
+Device Firmware Level: 003Q
55
+Shield Counter: 0
56
+Successful diagnostics completion on : N/A
57
+SAS Address(0): 0x4433221104000000
58
+Connected Port Number: 4(path0)
59
+Inquiry Data: S1YHNXAG804005 SAMSUNG MZ7LM960HCHP-00003 GXT3003Q
60
+FDE Capable: Not Capable
61
+FDE Enable: Disable
62
+Secured: Unsecured
63
+Locked: Unlocked
64
+Needs EKM Attention: No
65
+Foreign State: None
66
+Device Speed: 6.0Gb/s
67
+Link Speed: 6.0Gb/s
68
+Media Type: Solid State Device
69
+Drive: Not Certified
70
+Drive Temperature :33C (91.40 F)
71
+PI Eligibility: No
72
+Drive is formatted for PI information: No
73
+PI: No PI
74
+Drive's NCQ setting : N/A
75
+Port-0 :
76
+Port status: Active
77
+Port's Linkspeed: 6.0Gb/s
78
+Drive has flagged a S.M.A.R.T alert : No
79
+
80
+
81
+
82
+
83
+PD: 1 Information
84
+Enclosure Device ID: 32
85
+Slot Number: 2
86
+Drive's position: DiskGroup: 0, Span: 0, Arm: 1
87
+Enclosure position: 1
88
+Device Id: 2
89
+WWN: 5002538c00019b96
90
+Sequence Number: 2
91
+Media Error Count: 0
92
+Other Error Count: 0
93
+Predictive Failure Count: 0
94
+Last Predictive Failure Event Seq Number: 0
95
+PD Type: SATA
96
+
97
+Raw Size: 894.252 GB [0x6fc81ab0 Sectors]
98
+Non Coerced Size: 893.752 GB [0x6fb81ab0 Sectors]
99
+Coerced Size: 893.75 GB [0x6fb80000 Sectors]
100
+Sector Size: 512
101
+Logical Sector Size: 512
102
+Physical Sector Size: 512
103
+Firmware state: Online, Spun Up
104
+Device Firmware Level: 003Q
105
+Shield Counter: 0
106
+Successful diagnostics completion on : N/A
107
+SAS Address(0): 0x4433221106000000
108
+Connected Port Number: 6(path0)
109
+Inquiry Data: S1YHNYAG600061 SAMSUNG MZ7LM960HCHP-00003 GXT3003Q
110
+FDE Capable: Not Capable
111
+FDE Enable: Disable
112
+Secured: Unsecured
113
+Locked: Unlocked
114
+Needs EKM Attention: No
115
+Foreign State: None
116
+Device Speed: 6.0Gb/s
117
+Link Speed: 6.0Gb/s
118
+Media Type: Solid State Device
119
+Drive: Not Certified
120
+Drive Temperature :33C (91.40 F)
121
+PI Eligibility: No
122
+Drive is formatted for PI information: No
123
+PI: No PI
124
+Drive's NCQ setting : N/A
125
+Port-0 :
126
+Port status: Active
127
+Port's Linkspeed: 6.0Gb/s
128
+Drive has flagged a S.M.A.R.T alert : No
129
+
130
+
131
+
132
+
133
+PD: 2 Information
134
+Enclosure Device ID: 32
135
+Slot Number: 1
136
+Drive's position: DiskGroup: 0, Span: 0, Arm: 2
137
+Enclosure position: 1
138
+Device Id: 1
139
+WWN: 5002538c4002e707
140
+Sequence Number: 2
141
+Media Error Count: 0
142
+Other Error Count: 0
143
+Predictive Failure Count: 0
144
+Last Predictive Failure Event Seq Number: 0
145
+PD Type: SATA
146
+
147
+Raw Size: 894.252 GB [0x6fc81ab0 Sectors]
148
+Non Coerced Size: 893.752 GB [0x6fb81ab0 Sectors]
149
+Coerced Size: 893.75 GB [0x6fb80000 Sectors]
150
+Sector Size: 512
151
+Logical Sector Size: 512
152
+Physical Sector Size: 512
153
+Firmware state: Online, Spun Up
154
+Device Firmware Level: 003Q
155
+Shield Counter: 0
156
+Successful diagnostics completion on : N/A
157
+SAS Address(0): 0x4433221100000000
158
+Connected Port Number: 0(path0)
159
+Inquiry Data: S1YHNXAG803993 SAMSUNG MZ7LM960HCHP-00003 GXT3003Q
160
+FDE Capable: Not Capable
161
+FDE Enable: Disable
162
+Secured: Unsecured
163
+Locked: Unlocked
164
+Needs EKM Attention: No
165
+Foreign State: None
166
+Device Speed: 6.0Gb/s
167
+Link Speed: 6.0Gb/s
168
+Media Type: Solid State Device
169
+Drive: Not Certified
170
+Drive Temperature :34C (93.20 F)
171
+PI Eligibility: No
172
+Drive is formatted for PI information: No
173
+PI: No PI
174
+Drive's NCQ setting : N/A
175
+Port-0 :
176
+Port status: Active
177
+Port's Linkspeed: 6.0Gb/s
178
+Drive has flagged a S.M.A.R.T alert : No
179
+
180
+
181
+
182
+
183
+PD: 3 Information
184
+Enclosure Device ID: 32
185
+Slot Number: 3
186
+Drive's position: DiskGroup: 0, Span: 0, Arm: 3
187
+Enclosure position: 1
188
+Device Id: 3
189
+WWN: 5002538c4002e70f
190
+Sequence Number: 2
191
+Media Error Count: 0
192
+Other Error Count: 0
193
+Predictive Failure Count: 0
194
+Last Predictive Failure Event Seq Number: 0
195
+PD Type: SATA
196
+
197
+Raw Size: 894.252 GB [0x6fc81ab0 Sectors]
198
+Non Coerced Size: 893.752 GB [0x6fb81ab0 Sectors]
199
+Coerced Size: 893.75 GB [0x6fb80000 Sectors]
200
+Sector Size: 512
201
+Logical Sector Size: 512
202
+Physical Sector Size: 512
203
+Firmware state: Online, Spun Up
204
+Device Firmware Level: 003Q
205
+Shield Counter: 0
206
+Successful diagnostics completion on : N/A
207
+SAS Address(0): 0x4433221102000000
208
+Connected Port Number: 2(path0)
209
+Inquiry Data: S1YHNXAG804001 SAMSUNG MZ7LM960HCHP-00003 GXT3003Q
210
+FDE Capable: Not Capable
211
+FDE Enable: Disable
212
+Secured: Unsecured
213
+Locked: Unlocked
214
+Needs EKM Attention: No
215
+Foreign State: None
216
+Device Speed: 6.0Gb/s
217
+Link Speed: 6.0Gb/s
218
+Media Type: Solid State Device
219
+Drive: Not Certified
220
+Drive Temperature :34C (93.20 F)
221
+PI Eligibility: No
222
+Drive is formatted for PI information: No
223
+PI: No PI
224
+Drive's NCQ setting : N/A
225
+Port-0 :
226
+Port status: Active
227
+Port's Linkspeed: 6.0Gb/s
228
+Drive has flagged a S.M.A.R.T alert : No
229
+
230
+
231
+
232
+
233
+PD: 4 Information
234
+Enclosure Device ID: 32
235
+Slot Number: 5
236
+Drive's position: DiskGroup: 0, Span: 0, Arm: 4
237
+Enclosure position: 1
238
+Device Id: 5
239
+WWN: 5002538c4002e712
240
+Sequence Number: 2
241
+Media Error Count: 0
242
+Other Error Count: 0
243
+Predictive Failure Count: 0
244
+Last Predictive Failure Event Seq Number: 0
245
+PD Type: SATA
246
+
247
+Raw Size: 894.252 GB [0x6fc81ab0 Sectors]
248
+Non Coerced Size: 893.752 GB [0x6fb81ab0 Sectors]
249
+Coerced Size: 893.75 GB [0x6fb80000 Sectors]
250
+Sector Size: 512
251
+Logical Sector Size: 512
252
+Physical Sector Size: 512
253
+Firmware state: Online, Spun Up
254
+Device Firmware Level: 003Q
255
+Shield Counter: 0
256
+Successful diagnostics completion on : N/A
257
+SAS Address(0): 0x4433221101000000
258
+Connected Port Number: 1(path0)
259
+Inquiry Data: S1YHNXAG804004 SAMSUNG MZ7LM960HCHP-00003 GXT3003Q
260
+FDE Capable: Not Capable
261
+FDE Enable: Disable
262
+Secured: Unsecured
263
+Locked: Unlocked
264
+Needs EKM Attention: No
265
+Foreign State: None
266
+Device Speed: 6.0Gb/s
267
+Link Speed: 6.0Gb/s
268
+Media Type: Solid State Device
269
+Drive: Not Certified
270
+Drive Temperature :34C (93.20 F)
271
+PI Eligibility: No
272
+Drive is formatted for PI information: No
273
+PI: No PI
274
+Drive's NCQ setting : N/A
275
+Port-0 :
276
+Port status: Active
277
+Port's Linkspeed: 6.0Gb/s
278
+Drive has flagged a S.M.A.R.T alert : No
279
+
280
+
281
+
282
+
283
+PD: 5 Information
284
+Enclosure Device ID: 32
285
+Slot Number: 4
286
+Drive's position: DiskGroup: 0, Span: 0, Arm: 5
287
+Enclosure position: 1
288
+Device Id: 4
289
+WWN: 5002538c4002e6e9
290
+Sequence Number: 2
291
+Media Error Count: 0
292
+Other Error Count: 0
293
+Predictive Failure Count: 0
294
+Last Predictive Failure Event Seq Number: 0
295
+PD Type: SATA
296
+
297
+Raw Size: 894.252 GB [0x6fc81ab0 Sectors]
298
+Non Coerced Size: 893.752 GB [0x6fb81ab0 Sectors]
299
+Coerced Size: 893.75 GB [0x6fb80000 Sectors]
300
+Sector Size: 512
301
+Logical Sector Size: 512
302
+Physical Sector Size: 512
303
+Firmware state: Online, Spun Up
304
+Device Firmware Level: 003Q
305
+Shield Counter: 0
306
+Successful diagnostics completion on : N/A
307
+SAS Address(0): 0x4433221105000000
308
+Connected Port Number: 5(path0)
309
+Inquiry Data: S1YHNXAG803963 SAMSUNG MZ7LM960HCHP-00003 GXT3003Q
310
+FDE Capable: Not Capable
311
+FDE Enable: Disable
312
+Secured: Unsecured
313
+Locked: Unlocked
314
+Needs EKM Attention: No
315
+Foreign State: None
316
+Device Speed: 6.0Gb/s
317
+Link Speed: 6.0Gb/s
318
+Media Type: Solid State Device
319
+Drive: Not Certified
320
+Drive Temperature :33C (91.40 F)
321
+PI Eligibility: No
322
+Drive is formatted for PI information: No
323
+PI: No PI
324
+Drive's NCQ setting : N/A
325
+Port-0 :
326
+Port status: Active
327
+Port's Linkspeed: 6.0Gb/s
328
+Drive has flagged a S.M.A.R.T alert : No
329
+
330
+
331
+
332
+
333
+PD: 6 Information
334
+Enclosure Device ID: 32
335
+Slot Number: 6
336
+Drive's position: DiskGroup: 0, Span: 0, Arm: 6
337
+Enclosure position: 1
338
+Device Id: 6
339
+WWN: 5002538c4002da83
340
+Sequence Number: 2
341
+Media Error Count: 0
342
+Other Error Count: 0
343
+Predictive Failure Count: 0
344
+Last Predictive Failure Event Seq Number: 0
345
+PD Type: SATA
346
+
347
+Raw Size: 894.252 GB [0x6fc81ab0 Sectors]
348
+Non Coerced Size: 893.752 GB [0x6fb81ab0 Sectors]
349
+Coerced Size: 893.75 GB [0x6fb80000 Sectors]
350
+Sector Size: 512
351
+Logical Sector Size: 512
352
+Physical Sector Size: 512
353
+Firmware state: Online, Spun Up
354
+Device Firmware Level: 003Q
355
+Shield Counter: 0
356
+Successful diagnostics completion on : N/A
357
+SAS Address(0): 0x4433221107000000
358
+Connected Port Number: 7(path0)
359
+Inquiry Data: S1YHNXAG801029 SAMSUNG MZ7LM960HCHP-00003 GXT3003Q
360
+FDE Capable: Not Capable
361
+FDE Enable: Disable
362
+Secured: Unsecured
363
+Locked: Unlocked
364
+Needs EKM Attention: No
365
+Foreign State: None
366
+Device Speed: 6.0Gb/s
367
+Link Speed: 6.0Gb/s
368
+Media Type: Solid State Device
369
+Drive: Not Certified
370
+Drive Temperature :33C (91.40 F)
371
+PI Eligibility: No
372
+Drive is formatted for PI information: No
373
+PI: No PI
374
+Drive's NCQ setting : N/A
375
+Port-0 :
376
+Port status: Active
377
+Port's Linkspeed: 6.0Gb/s
378
+Drive has flagged a S.M.A.R.T alert : No
379
+
380
+
381
+
382
+
383
+PD: 7 Information
384
+Enclosure Device ID: 32
385
+Slot Number: 7
386
+Drive's position: DiskGroup: 0, Span: 0, Arm: 7
387
+Enclosure position: 1
388
+Device Id: 7
389
+WWN: 5002538c4002dade
390
+Sequence Number: 2
391
+Media Error Count: 0
392
+Other Error Count: 0
393
+Predictive Failure Count: 0
394
+Last Predictive Failure Event Seq Number: 0
395
+PD Type: SATA
396
+
397
+Raw Size: 894.252 GB [0x6fc81ab0 Sectors]
398
+Non Coerced Size: 893.752 GB [0x6fb81ab0 Sectors]
399
+Coerced Size: 893.75 GB [0x6fb80000 Sectors]
400
+Sector Size: 512
401
+Logical Sector Size: 512
402
+Physical Sector Size: 512
403
+Firmware state: Online, Spun Up
404
+Device Firmware Level: 003Q
405
+Shield Counter: 0
406
+Successful diagnostics completion on : N/A
407
+SAS Address(0): 0x4433221103000000
408
+Connected Port Number: 3(path0)
409
+Inquiry Data: S1YHNXAG801120 SAMSUNG MZ7LM960HCHP-00003 GXT3003Q
410
+FDE Capable: Not Capable
411
+FDE Enable: Disable
412
+Secured: Unsecured
413
+Locked: Unlocked
414
+Needs EKM Attention: No
415
+Foreign State: None
416
+Device Speed: 6.0Gb/s
417
+Link Speed: 6.0Gb/s
418
+Media Type: Solid State Device
419
+Drive: Not Certified
420
+Drive Temperature :34C (93.20 F)
421
+PI Eligibility: No
422
+Drive is formatted for PI information: No
423
+PI: No PI
424
+Drive's NCQ setting : N/A
425
+Port-0 :
426
+Port status: Active
427
+Port's Linkspeed: 6.0Gb/s
428
+Drive has flagged a S.M.A.R.T alert : No
429
+
430
+
431
+
432
+
433
+Exit Code: 0x00
\ No newline at end of file
src/health/health.d/megacli.conf
+82
-5
@@ -1,5 +1,86 @@
1
+# you can disable an alarm notification by setting the 'to' line to: silent
2
2
-## Adapters (controllers)
3
+# go.d/megacli
4
+
5
+# Adapters (controllers)
6
+
7
+ template: megacli_adapter_health_state
8
+ on: megacli.adapter_health_state
9
+ class: Errors
10
+ type: System
11
+component: RAID
12
+ lookup: average -1m unaligned percentage of optimal
13
+ units: %
14
+ every: 10s
15
+ crit: $this < 100
16
+ delay: down 5m multiplier 2 max 10m
17
+ summary: MegaCLI adapter ${label:adapter_number} health
18
+ info: MegaCLI adapter ${label:adapter_number} is in the degraded state
19
+ to: sysadmin
20
+
21
+ template: megacli_phys_drive_media_errors
22
+ on: megacli.phys_drive_media_errors_rate
23
+ class: Errors
24
+ type: System
25
+component: RAID
26
+ lookup: sum -10s
27
+ units: media errors
28
+ every: 10s
29
+ warn: $this > 0
30
+ delay: up 1m down 5m multiplier 2 max 10m
31
+ summary: MegaCLI PD adapter ${label:adapter_number} slot ${label:slot_number} media errors
32
+ info: MegaCLI physical drive adapter ${label:adapter_number} slot ${label:slot_number} media errors
33
+ to: sysadmin
34
+
35
+# Physical Drives
36
+
37
+ template: megacli_phys_drive_predictive_failures
38
+ on: megacli.phys_drive_predictive_failures
39
+ class: Errors
40
+ type: System
41
+component: RAID
42
+ lookup: sum -10s
43
+ units: media errors
44
+ every: 10s
45
+ warn: $this > 0
46
+ delay: up 1m down 5m multiplier 2 max 10m
47
+ summary: MegaCLI PD adapter ${label:adapter_number} slot ${label:slot_number} predictive failures
48
+ info: MegaCLI physical drive (adapter ${label:adapter_number} slot ${label:slot_number}) predictive failures
49
+ to: sysadmin
50
+
51
+# Backup Battery Unit
52
+
53
+ template: megacli_bbu_charge
54
+ on: megacli.bbu_charge
55
+ class: Workload
56
+ type: System
57
+component: RAID
58
+ lookup: average -10s
59
+ units: percent
60
+ every: 10s
61
+ warn: $this <= (($status >= $WARNING) ? (85) : (80))
62
+ crit: $this <= (($status == $CRITICAL) ? (50) : (40))
63
+ summary: MegaCLI BBU charge
64
+ info: MegaCLI Backup Battery Unit (adapter ${label:adapter_number}) average charge over the last minute
65
+ to: sysadmin
66
+
67
+ template: megacli_bbu_recharge_cycles
68
+ on: megacli.bbu_recharge_cycles
69
+ class: Workload
70
+ type: System
71
+component: RAID
72
+ lookup: average -10s
73
+ units: cycles
74
+ every: 10s
75
+ warn: $this >= 100
76
+ crit: $this >= 500
77
+ summary: MegaCLI BBU recharge cycles
78
+ info: MegaCLI Backup Battery Unit (adapter ${label:adapter_number}) recharge cycles
79
+ to: sysadmin
80
+
81
+# --------------------------------------------------------------------------------------------------------------------
82
+
83
+# python.d/megacli
84
85
template: megacli_adapter_state
86
on: megacli.adapter_degraded
@@ -15,8 +96,6 @@ component: RAID
96
info: Adapter is in the degraded state (0: false, 1: true)
97
to: sysadmin
98
18
-## Physical Disks
19
-
99
template: megacli_pd_predictive_failures
100
on: megacli.pd_predictive_failure
101
class: Errors
@@ -45,8 +124,6 @@ component: RAID
124
info: Number of physical drive media errors
125
to: sysadmin
126
48
-## Battery Backup Units (BBU)
49
-
127
template: megacli_bbu_relative_charge
128
on: megacli.bbu_relative_charge
129
class: Workload