summaryrefslogtreecommitdiffstats
path: root/src/go/plugin/go.d/modules/storcli/collect_drives.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/go/plugin/go.d/modules/storcli/collect_drives.go237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/go/plugin/go.d/modules/storcli/collect_drives.go b/src/go/plugin/go.d/modules/storcli/collect_drives.go
new file mode 100644
index 000000000..95965d572
--- /dev/null
+++ b/src/go/plugin/go.d/modules/storcli/collect_drives.go
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package storcli
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+type drivesInfoResponse struct {
+ Controllers []struct {
+ CommandStatus struct {
+ Controller int `json:"Controller"`
+ Status string `json:"Status"`
+ } `json:"Command Status"`
+ ResponseData map[string]json.RawMessage `json:"Response Data"`
+ } `json:"Controllers"`
+}
+
+type (
+ driveInfo struct {
+ EIDSlt string `json:"EID:Slt"`
+ //DID int `json:"DID"`
+ //State string `json:"State"`
+ //DG int `json:"DG"` // FIX: can be integer or "-"
+ //Size string `json:"Size"`
+ //Intf string `json:"Intf"`
+ Med string `json:"Med"`
+ //SED string `json:"SED"`
+ //PI string `json:"PI"`
+ //SeSz string `json:"SeSz"`
+ //Model string `json:"Model"`
+ //Sp string `json:"Sp"`
+ //Type string `json:"Type"`
+ }
+ driveState struct {
+ MediaErrorCount storNumber `json:"Media Error Count"`
+ OtherErrorCount storNumber `json:"Other Error Count"`
+ DriveTemperature string `json:"Drive Temperature"`
+ PredictiveFailureCount storNumber `json:"Predictive Failure Count"`
+ SmartAlertFlagged string `json:"S.M.A.R.T alert flagged by drive"`
+ }
+ driveAttrs struct {
+ WWN string `json:"WWN"`
+ DeviceSpeed string `json:"Device Speed"`
+ LinkSpeed string `json:"Link Speed"`
+ }
+)
+
+type storNumber string // some int values can be 'N/A'
+
+func (n *storNumber) UnmarshalJSON(b []byte) error { *n = storNumber(b); return nil }
+
+func (s *StorCli) collectMegaRaidDrives(mx map[string]int64, resp *drivesInfoResponse) error {
+ if resp == nil {
+ return nil
+ }
+
+ for _, cntrl := range resp.Controllers {
+ var ids []string
+ for k := range cntrl.ResponseData {
+ if !strings.HasSuffix(k, "Detailed Information") {
+ continue
+ }
+ parts := strings.Fields(k) // Drive /c0/e252/s0 - Detailed Information
+ if len(parts) < 2 {
+ continue
+ }
+ id := parts[1]
+ if strings.IndexByte(id, '/') == -1 {
+ continue
+ }
+ ids = append(ids, id)
+ }
+
+ cntrlIdx := cntrl.CommandStatus.Controller
+
+ for _, id := range ids {
+ info, err := getDriveInfo(cntrl.ResponseData, id)
+ if err != nil {
+ return err
+ }
+ data, err := getDriveDetailedInfo(cntrl.ResponseData, id)
+ if err != nil {
+ return err
+ }
+ state, err := getDriveState(data, id)
+ if err != nil {
+ return err
+ }
+ attrs, err := getDriveAttrs(data, id)
+ if err != nil {
+ return err
+ }
+
+ if attrs.WWN == "" {
+ continue
+ }
+
+ if !s.drives[attrs.WWN] {
+ s.drives[attrs.WWN] = true
+ s.addPhysDriveCharts(cntrlIdx, info, state, attrs)
+ }
+
+ px := fmt.Sprintf("phys_drive_%s_cntrl_%d_", attrs.WWN, cntrlIdx)
+
+ if v, ok := parseInt(string(state.MediaErrorCount)); ok {
+ mx[px+"media_error_count"] = v
+ }
+ if v, ok := parseInt(string(state.OtherErrorCount)); ok {
+ mx[px+"other_error_count"] = v
+ }
+ if v, ok := parseInt(string(state.PredictiveFailureCount)); ok {
+ mx[px+"predictive_failure_count"] = v
+ }
+ if v, ok := parseInt(getTemperature(state.DriveTemperature)); ok {
+ mx[px+"temperature"] = v
+ }
+ for _, st := range []string{"active", "inactive"} {
+ mx[px+"smart_alert_status_"+st] = 0
+ }
+ if state.SmartAlertFlagged == "Yes" {
+ mx[px+"smart_alert_status_active"] = 1
+ } else {
+ mx[px+"smart_alert_status_inactive"] = 1
+ }
+ }
+ }
+
+ return nil
+}
+
+func (s *StorCli) queryDrivesInfo() (*drivesInfoResponse, error) {
+ bs, err := s.exec.drivesInfo()
+ if err != nil {
+ return nil, err
+ }
+
+ if len(bs) == 0 {
+ return nil, errors.New("empty response")
+ }
+
+ var resp drivesInfoResponse
+ if err := json.Unmarshal(bs, &resp); err != nil {
+ return nil, err
+ }
+
+ if len(resp.Controllers) == 0 {
+ return nil, errors.New("no controllers found")
+ }
+ if st := resp.Controllers[0].CommandStatus.Status; st != "Success" {
+ return nil, fmt.Errorf("command status error: %s", st)
+ }
+
+ return &resp, nil
+}
+
+func getDriveInfo(respData map[string]json.RawMessage, id string) (*driveInfo, error) {
+ k := fmt.Sprintf("Drive %s", id)
+ raw, ok := respData[k]
+ if !ok {
+ return nil, fmt.Errorf("drive info not found for '%s'", id)
+ }
+
+ var drive []driveInfo
+ if err := json.Unmarshal(raw, &drive); err != nil {
+ return nil, err
+ }
+
+ if len(drive) == 0 {
+ return nil, fmt.Errorf("drive info not found for '%s'", id)
+ }
+
+ return &drive[0], nil
+}
+
+func getDriveDetailedInfo(respData map[string]json.RawMessage, id string) (map[string]json.RawMessage, error) {
+ k := fmt.Sprintf("Drive %s - Detailed Information", id)
+ raw, ok := respData[k]
+ if !ok {
+ return nil, fmt.Errorf("drive detailed info not found for '%s'", id)
+ }
+
+ var info map[string]json.RawMessage
+ if err := json.Unmarshal(raw, &info); err != nil {
+ return nil, err
+ }
+
+ return info, nil
+}
+
+func getDriveState(driveDetailedInfo map[string]json.RawMessage, id string) (*driveState, error) {
+ k := fmt.Sprintf("Drive %s State", id)
+ raw, ok := driveDetailedInfo[k]
+ if !ok {
+ return nil, fmt.Errorf("drive detailed info state not found for '%s'", id)
+ }
+
+ var state driveState
+ if err := json.Unmarshal(raw, &state); err != nil {
+ return nil, err
+ }
+
+ return &state, nil
+}
+
+func getDriveAttrs(driveDetailedInfo map[string]json.RawMessage, id string) (*driveAttrs, error) {
+ k := fmt.Sprintf("Drive %s Device attributes", id)
+ raw, ok := driveDetailedInfo[k]
+ if !ok {
+ return nil, fmt.Errorf("drive detailed info state not found for '%s'", id)
+ }
+
+ var state driveAttrs
+ if err := json.Unmarshal(raw, &state); err != nil {
+ return nil, err
+ }
+
+ return &state, nil
+}
+
+func getTemperature(temp string) string {
+ // ' 28C (82.40 F)' (drive) or '33C' (bbu)
+ i := strings.IndexByte(temp, 'C')
+ if i == -1 {
+ return ""
+ }
+ return strings.TrimSpace(temp[:i])
+}
+
+func parseInt(s string) (int64, bool) {
+ i, err := strconv.ParseInt(s, 10, 64)
+ return i, err == nil
+}