bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/scollector/collectors/disk.go (about) 1 package collectors 2 3 // things that are not OS specific. 4 // ie: can compile and execute anywhere 5 6 import ( 7 "bufio" 8 "io" 9 "strconv" 10 "strings" 11 12 "bosun.org/metadata" 13 "bosun.org/opentsdb" 14 ) 15 16 // Check mdadm raid arrays on linux 17 // linux.disk.mdadm.state values: 18 19 type mdadmState int 20 21 const ( 22 mdadmUnknown mdadmState = 0 23 mdadmNormal = 1 // active or clean state 24 mdadmFailed = 2 // raid is failed 25 mdadmDegraded = 3 // raid is degraded 26 ) 27 28 const ( 29 mdadmDesc = "raid 0: unknown, 1: normal, 2: failed, 3: degraded" 30 syncDesc = "percent of spindles synchronization. 100% is fully synced" 31 spinDesc = "spin 0: failed, 1: active, 2: spare" 32 ) 33 34 type spinState int 35 36 // Check individual spindle disks in the array 37 const ( 38 spinFailed spinState = 0 39 spinActive = 1 40 spinSpare = 2 41 ) 42 43 type volumeDetail struct { 44 syncProgress float32 // progress: between 0..100% 45 state mdadmState 46 failedSpindle []string 47 activeSpindle []string 48 spareSpindle []string 49 } 50 51 func getResync(l string) (progress float32, gotit bool) { 52 prefix := "Rebuild Status : " 53 if !strings.HasPrefix(l, prefix) { 54 return 0, false 55 } 56 l = strings.TrimPrefix(l, prefix) 57 pcentIdx := strings.Index(l, "%") 58 if pcentIdx == -1 { 59 return 0, false 60 } 61 62 f, err := strconv.ParseFloat(l[:pcentIdx], 32) 63 return float32(f), err == nil 64 } 65 66 func getSpindle(l string) (dev string, gotit bool) { 67 fields := strings.Split(l, " ") 68 size := len(fields) 69 if strings.Contains(fields[size-1], "/dev/sd") { 70 return fields[size-1], true 71 } 72 return "", false 73 } 74 75 func getState(l string) (state mdadmState, gotit bool) { 76 if !strings.HasPrefix(l, "State : ") { 77 return mdadmUnknown, false 78 } 79 if strings.Contains(l, ", FAILED") { 80 return mdadmFailed, true 81 } 82 if strings.Contains(l, ", degraded") { 83 return mdadmDegraded, true 84 } 85 if strings.Contains(l, "clean") || strings.Contains(l, "active") { 86 return mdadmNormal, true 87 } 88 return mdadmUnknown, false 89 } 90 91 func parseExamineMdadm(examine io.Reader) (detail volumeDetail) { 92 scanner := bufio.NewScanner(examine) 93 // if there is no progress spotted, assume the disks are in sync 94 detail.syncProgress = 100.0 95 for scanner.Scan() { 96 l := scanner.Text() 97 l = strings.Trim(l, " \t") 98 99 // extract resync status 100 if progress, ok := getResync(l); ok { 101 detail.syncProgress = progress 102 } 103 104 // extract spindles 105 if dev, ok := getSpindle(l); ok { 106 if strings.Contains(l, "active sync") { 107 detail.activeSpindle = append(detail.activeSpindle, dev) 108 } else if strings.Contains(l, "spare") { 109 detail.spareSpindle = append(detail.spareSpindle, dev) 110 } else { // if we don't know, assume it's failed 111 detail.failedSpindle = append(detail.failedSpindle, dev) 112 } 113 } 114 115 // filter State 116 if state, ok := getState(l); ok { 117 detail.state = state 118 // consider failed arrays as 0% resynced 119 if state != mdadmNormal && state != mdadmDegraded { 120 detail.syncProgress = 0.0 121 } 122 } 123 } 124 return detail 125 } 126 127 func addMetricSpindle(md *opentsdb.MultiDataPoint, names []string, status spinState, volume string) { 128 metric := "linux.disk.mdadm.spindle" 129 for _, name := range names { 130 tags := opentsdb.TagSet{ 131 "volume": volume, 132 "spindle": name, 133 } 134 Add(md, metric, int(status), tags, metadata.Gauge, metadata.StatusCode, spinDesc) 135 } 136 } 137 138 func addMdadmMetric(md *opentsdb.MultiDataPoint, volume string, detail volumeDetail) { 139 tags := opentsdb.TagSet{"volume": volume} 140 metric := "linux.disk.mdadm.state" 141 Add(md, metric, detail.state, tags, metadata.Gauge, metadata.StatusCode, mdadmDesc) 142 143 metric = "linux.disk.mdadm.sync" 144 Add(md, metric, detail.syncProgress, tags, metadata.Gauge, metadata.Pct, syncDesc) 145 146 addMetricSpindle(md, detail.failedSpindle, spinFailed, volume) 147 addMetricSpindle(md, detail.activeSpindle, spinActive, volume) 148 addMetricSpindle(md, detail.spareSpindle, spinSpare, volume) 149 }