191 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
| // +build linux
 | |
| 
 | |
| package dmcache
 | |
| 
 | |
| import (
 | |
| 	"os/exec"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 
 | |
| 	"errors"
 | |
| 
 | |
| 	"github.com/influxdata/telegraf"
 | |
| )
 | |
| 
 | |
| const metricName = "dmcache"
 | |
| 
 | |
| type cacheStatus struct {
 | |
| 	device            string
 | |
| 	length            int64
 | |
| 	target            string
 | |
| 	metadataBlocksize int64
 | |
| 	metadataUsed      int64
 | |
| 	metadataTotal     int64
 | |
| 	cacheBlocksize    int64
 | |
| 	cacheUsed         int64
 | |
| 	cacheTotal        int64
 | |
| 	readHits          int64
 | |
| 	readMisses        int64
 | |
| 	writeHits         int64
 | |
| 	writeMisses       int64
 | |
| 	demotions         int64
 | |
| 	promotions        int64
 | |
| 	dirty             int64
 | |
| }
 | |
| 
 | |
| func (c *DMCache) Gather(acc telegraf.Accumulator) error {
 | |
| 	outputLines, err := c.getCurrentStatus()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	totalStatus := cacheStatus{}
 | |
| 
 | |
| 	for _, s := range outputLines {
 | |
| 		status, err := parseDMSetupStatus(s)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if c.PerDevice {
 | |
| 			tags := map[string]string{"device": status.device}
 | |
| 			acc.AddFields(metricName, toFields(status), tags)
 | |
| 		}
 | |
| 		aggregateStats(&totalStatus, status)
 | |
| 	}
 | |
| 
 | |
| 	acc.AddFields(metricName, toFields(totalStatus), map[string]string{"device": "all"})
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func parseDMSetupStatus(line string) (cacheStatus, error) {
 | |
| 	var err error
 | |
| 	parseError := errors.New("Output from dmsetup could not be parsed")
 | |
| 	status := cacheStatus{}
 | |
| 	values := strings.Fields(line)
 | |
| 	if len(values) < 15 {
 | |
| 		return cacheStatus{}, parseError
 | |
| 	}
 | |
| 
 | |
| 	status.device = strings.TrimRight(values[0], ":")
 | |
| 	status.length, err = strconv.ParseInt(values[2], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.target = values[3]
 | |
| 	status.metadataBlocksize, err = strconv.ParseInt(values[4], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	metadata := strings.Split(values[5], "/")
 | |
| 	if len(metadata) != 2 {
 | |
| 		return cacheStatus{}, parseError
 | |
| 	}
 | |
| 	status.metadataUsed, err = strconv.ParseInt(metadata[0], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.metadataTotal, err = strconv.ParseInt(metadata[1], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.cacheBlocksize, err = strconv.ParseInt(values[6], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	cache := strings.Split(values[7], "/")
 | |
| 	if len(cache) != 2 {
 | |
| 		return cacheStatus{}, parseError
 | |
| 	}
 | |
| 	status.cacheUsed, err = strconv.ParseInt(cache[0], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.cacheTotal, err = strconv.ParseInt(cache[1], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.readHits, err = strconv.ParseInt(values[8], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.readMisses, err = strconv.ParseInt(values[9], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.writeHits, err = strconv.ParseInt(values[10], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.writeMisses, err = strconv.ParseInt(values[11], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.demotions, err = strconv.ParseInt(values[12], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.promotions, err = strconv.ParseInt(values[13], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 	status.dirty, err = strconv.ParseInt(values[14], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return cacheStatus{}, err
 | |
| 	}
 | |
| 
 | |
| 	return status, nil
 | |
| }
 | |
| 
 | |
| func aggregateStats(totalStatus *cacheStatus, status cacheStatus) {
 | |
| 	totalStatus.length += status.length
 | |
| 	totalStatus.metadataBlocksize += status.metadataBlocksize
 | |
| 	totalStatus.metadataUsed += status.metadataUsed
 | |
| 	totalStatus.metadataTotal += status.metadataTotal
 | |
| 	totalStatus.cacheBlocksize += status.cacheBlocksize
 | |
| 	totalStatus.cacheUsed += status.cacheUsed
 | |
| 	totalStatus.cacheTotal += status.cacheTotal
 | |
| 	totalStatus.readHits += status.readHits
 | |
| 	totalStatus.readMisses += status.readMisses
 | |
| 	totalStatus.writeHits += status.writeHits
 | |
| 	totalStatus.writeMisses += status.writeMisses
 | |
| 	totalStatus.demotions += status.demotions
 | |
| 	totalStatus.promotions += status.promotions
 | |
| 	totalStatus.dirty += status.dirty
 | |
| }
 | |
| 
 | |
| func toFields(status cacheStatus) map[string]interface{} {
 | |
| 	fields := make(map[string]interface{})
 | |
| 	fields["length"] = status.length
 | |
| 	fields["metadata_blocksize"] = status.metadataBlocksize
 | |
| 	fields["metadata_used"] = status.metadataUsed
 | |
| 	fields["metadata_total"] = status.metadataTotal
 | |
| 	fields["cache_blocksize"] = status.cacheBlocksize
 | |
| 	fields["cache_used"] = status.cacheUsed
 | |
| 	fields["cache_total"] = status.cacheTotal
 | |
| 	fields["read_hits"] = status.readHits
 | |
| 	fields["read_misses"] = status.readMisses
 | |
| 	fields["write_hits"] = status.writeHits
 | |
| 	fields["write_misses"] = status.writeMisses
 | |
| 	fields["demotions"] = status.demotions
 | |
| 	fields["promotions"] = status.promotions
 | |
| 	fields["dirty"] = status.dirty
 | |
| 	return fields
 | |
| }
 | |
| 
 | |
| func dmSetupStatus() ([]string, error) {
 | |
| 	out, err := exec.Command("/bin/sh", "-c", "sudo /sbin/dmsetup status --target cache").Output()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if string(out) == "No devices found\n" {
 | |
| 		return []string{}, nil
 | |
| 	}
 | |
| 
 | |
| 	outString := strings.TrimRight(string(out), "\n")
 | |
| 	status := strings.Split(outString, "\n")
 | |
| 
 | |
| 	return status, nil
 | |
| }
 |