182 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			182 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
package nagios
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"log"
 | 
						|
	"regexp"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/influxdata/telegraf"
 | 
						|
	"github.com/influxdata/telegraf/metric"
 | 
						|
)
 | 
						|
 | 
						|
type NagiosParser struct {
 | 
						|
	MetricName  string
 | 
						|
	DefaultTags map[string]string
 | 
						|
}
 | 
						|
 | 
						|
// Got from Alignak
 | 
						|
// https://github.com/Alignak-monitoring/alignak/blob/develop/alignak/misc/perfdata.py
 | 
						|
var (
 | 
						|
	perfSplitRegExp = regexp.MustCompile(`([^=]+=\S+)`)
 | 
						|
	nagiosRegExp    = regexp.MustCompile(`^([^=]+)=([\d\.\-\+eE]+)([\w\/%]*);?([\d\.\-\+eE:~@]+)?;?([\d\.\-\+eE:~@]+)?;?([\d\.\-\+eE]+)?;?([\d\.\-\+eE]+)?;?\s*`)
 | 
						|
)
 | 
						|
 | 
						|
func (p *NagiosParser) ParseLine(line string) (telegraf.Metric, error) {
 | 
						|
	metrics, err := p.Parse([]byte(line))
 | 
						|
	return metrics[0], err
 | 
						|
}
 | 
						|
 | 
						|
func (p *NagiosParser) SetDefaultTags(tags map[string]string) {
 | 
						|
	p.DefaultTags = tags
 | 
						|
}
 | 
						|
 | 
						|
func (p *NagiosParser) Parse(buf []byte) ([]telegraf.Metric, error) {
 | 
						|
	metrics := make([]telegraf.Metric, 0)
 | 
						|
	lines := strings.Split(strings.TrimSpace(string(buf)), "\n")
 | 
						|
 | 
						|
	for _, line := range lines {
 | 
						|
		data_splitted := strings.Split(line, "|")
 | 
						|
 | 
						|
		if len(data_splitted) != 2 {
 | 
						|
			// got human readable output only or bad line
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		m, err := parsePerfData(data_splitted[1])
 | 
						|
		if err != nil {
 | 
						|
			log.Printf("E! [parser.nagios] failed to parse performance data: %s\n", err.Error())
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		metrics = append(metrics, m...)
 | 
						|
	}
 | 
						|
	return metrics, nil
 | 
						|
}
 | 
						|
 | 
						|
func parsePerfData(perfdatas string) ([]telegraf.Metric, error) {
 | 
						|
	metrics := make([]telegraf.Metric, 0)
 | 
						|
 | 
						|
	for _, unParsedPerf := range perfSplitRegExp.FindAllString(perfdatas, -1) {
 | 
						|
		trimedPerf := strings.TrimSpace(unParsedPerf)
 | 
						|
		perf := nagiosRegExp.FindStringSubmatch(trimedPerf)
 | 
						|
 | 
						|
		// verify at least `'label'=value[UOM];` existed
 | 
						|
		if len(perf) < 3 {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if perf[1] == "" || perf[2] == "" {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		fieldName := strings.Trim(perf[1], "'")
 | 
						|
		tags := map[string]string{"perfdata": fieldName}
 | 
						|
		if perf[3] != "" {
 | 
						|
			str := string(perf[3])
 | 
						|
			if str != "" {
 | 
						|
				tags["unit"] = str
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		fields := make(map[string]interface{})
 | 
						|
		if perf[2] == "U" {
 | 
						|
			return nil, errors.New("Value undetermined")
 | 
						|
		}
 | 
						|
 | 
						|
		f, err := strconv.ParseFloat(string(perf[2]), 64)
 | 
						|
		if err == nil {
 | 
						|
			fields["value"] = f
 | 
						|
		}
 | 
						|
		if perf[4] != "" {
 | 
						|
			low, high, err := parseThreshold(perf[4])
 | 
						|
			if err == nil {
 | 
						|
				if strings.Contains(perf[4], "@") {
 | 
						|
					fields["warning_le"] = low
 | 
						|
					fields["warning_ge"] = high
 | 
						|
				} else {
 | 
						|
					fields["warning_lt"] = low
 | 
						|
					fields["warning_gt"] = high
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if perf[5] != "" {
 | 
						|
			low, high, err := parseThreshold(perf[5])
 | 
						|
			if err == nil {
 | 
						|
				if strings.Contains(perf[5], "@") {
 | 
						|
					fields["critical_le"] = low
 | 
						|
					fields["critical_ge"] = high
 | 
						|
				} else {
 | 
						|
					fields["critical_lt"] = low
 | 
						|
					fields["critical_gt"] = high
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if perf[6] != "" {
 | 
						|
			f, err := strconv.ParseFloat(perf[6], 64)
 | 
						|
			if err == nil {
 | 
						|
				fields["min"] = f
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if perf[7] != "" {
 | 
						|
			f, err := strconv.ParseFloat(perf[7], 64)
 | 
						|
			if err == nil {
 | 
						|
				fields["max"] = f
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// Create metric
 | 
						|
		metric, err := metric.New("nagios", tags, fields, time.Now().UTC())
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		// Add Metric
 | 
						|
		metrics = append(metrics, metric)
 | 
						|
	}
 | 
						|
 | 
						|
	return metrics, nil
 | 
						|
}
 | 
						|
 | 
						|
// from math
 | 
						|
const (
 | 
						|
	MaxFloat64 = 1.797693134862315708145274237317043567981e+308 // 2**1023 * (2**53 - 1) / 2**52
 | 
						|
	MinFloat64 = 4.940656458412465441765687928682213723651e-324 // 1 / 2**(1023 - 1 + 52)
 | 
						|
)
 | 
						|
 | 
						|
var ErrBadThresholdFormat = errors.New("Bad threshold format")
 | 
						|
 | 
						|
// Handles all cases from https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT
 | 
						|
func parseThreshold(threshold string) (min float64, max float64, err error) {
 | 
						|
	thresh := strings.Split(threshold, ":")
 | 
						|
	switch len(thresh) {
 | 
						|
	case 1:
 | 
						|
		max, err = strconv.ParseFloat(string(thresh[0]), 64)
 | 
						|
		if err != nil {
 | 
						|
			return 0, 0, ErrBadThresholdFormat
 | 
						|
		}
 | 
						|
 | 
						|
		return 0, max, nil
 | 
						|
	case 2:
 | 
						|
		if thresh[0] == "~" {
 | 
						|
			min = MinFloat64
 | 
						|
		} else {
 | 
						|
			min, err = strconv.ParseFloat(string(thresh[0]), 64)
 | 
						|
			if err != nil {
 | 
						|
				min = 0
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if thresh[1] == "" {
 | 
						|
			max = MaxFloat64
 | 
						|
		} else {
 | 
						|
			max, err = strconv.ParseFloat(string(thresh[1]), 64)
 | 
						|
			if err != nil {
 | 
						|
				return 0, 0, ErrBadThresholdFormat
 | 
						|
			}
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		return 0, 0, ErrBadThresholdFormat
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 |