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  }