Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
17
plugins/parsers/nagios/README.md
Normal file
17
plugins/parsers/nagios/README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Nagios Parser Plugin
|
||||
|
||||
The `nagios` data format parses the output of nagios plugins.
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
[[inputs.exec]]
|
||||
## Commands array
|
||||
commands = ["/usr/lib/nagios/plugins/check_load -w 5,6,7 -c 7,8,9"]
|
||||
|
||||
## Data format to consume.
|
||||
## Each data format has its own unique set of configuration options, read
|
||||
## more about them here:
|
||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||
data_format = "nagios"
|
||||
```
|
323
plugins/parsers/nagios/parser.go
Normal file
323
plugins/parsers/nagios/parser.go
Normal file
|
@ -0,0 +1,323 @@
|
|||
package nagios
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/metric"
|
||||
"github.com/influxdata/telegraf/plugins/parsers"
|
||||
)
|
||||
|
||||
// unknownExitCode is the nagios unknown status code
|
||||
// the exit code should be used if an error occurs or something unexpected happens
|
||||
const unknownExitCode = 3
|
||||
|
||||
// getExitCode get the exit code from an error value which is the result
|
||||
// of running a command through exec package api.
|
||||
func getExitCode(err error) (int, error) {
|
||||
if err == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var ee *exec.ExitError
|
||||
if !errors.As(err, &ee) {
|
||||
return unknownExitCode, err
|
||||
}
|
||||
|
||||
ws, ok := ee.Sys().(syscall.WaitStatus)
|
||||
if !ok {
|
||||
return 0, errors.New("expected syscall.WaitStatus")
|
||||
}
|
||||
|
||||
return ws.ExitStatus(), nil
|
||||
}
|
||||
|
||||
// AddState adds a state derived from the runErr. Unknown state will be set as fallback.
|
||||
// If any error occurs, it is guaranteed to be added to the service output.
|
||||
// An updated slice of metrics will be returned.
|
||||
func AddState(runErr error, errMessage []byte, metrics []telegraf.Metric) []telegraf.Metric {
|
||||
state, exitErr := getExitCode(runErr)
|
||||
// This will ensure that in every error case the valid nagios state 'unknown' will be returned.
|
||||
// No error needs to be thrown because the output will contain the error information.
|
||||
// Description found at 'Plugin Return Codes' https://nagios-plugins.org/doc/guidelines.html
|
||||
if exitErr != nil || state < 0 || state > unknownExitCode {
|
||||
state = unknownExitCode
|
||||
}
|
||||
|
||||
for _, m := range metrics {
|
||||
if m.Name() == "nagios_state" {
|
||||
m.AddField("state", state)
|
||||
|
||||
if state == unknownExitCode {
|
||||
errorMessage := string(errMessage)
|
||||
if exitErr != nil && exitErr.Error() != "" {
|
||||
errorMessage = exitErr.Error()
|
||||
}
|
||||
value, ok := m.GetField("service_output")
|
||||
if !ok || value == "" {
|
||||
// By adding the error message as output, the metric contains all needed information to understand
|
||||
// the problem and fix it
|
||||
m.AddField("service_output", errorMessage)
|
||||
}
|
||||
}
|
||||
return metrics
|
||||
}
|
||||
}
|
||||
|
||||
var ts time.Time
|
||||
if len(metrics) != 0 {
|
||||
ts = metrics[0].Time()
|
||||
} else {
|
||||
ts = time.Now().UTC()
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"state": state,
|
||||
}
|
||||
m := metric.New("nagios_state", nil, f, ts)
|
||||
|
||||
return append(metrics, m)
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
DefaultTags map[string]string `toml:"-"`
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
metricName 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 *Parser) ParseLine(line string) (telegraf.Metric, error) {
|
||||
metrics, err := p.Parse([]byte(line))
|
||||
return metrics[0], err
|
||||
}
|
||||
|
||||
func (p *Parser) SetDefaultTags(tags map[string]string) {
|
||||
p.DefaultTags = tags
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(buf []byte) ([]telegraf.Metric, error) {
|
||||
ts := time.Now().UTC()
|
||||
|
||||
s := bufio.NewScanner(bytes.NewReader(buf))
|
||||
|
||||
var msg bytes.Buffer
|
||||
var longmsg bytes.Buffer
|
||||
|
||||
metrics := make([]telegraf.Metric, 0)
|
||||
|
||||
// Scan the first line.
|
||||
if !s.Scan() && s.Err() != nil {
|
||||
return nil, s.Err()
|
||||
}
|
||||
parts := bytes.Split(s.Bytes(), []byte{'|'})
|
||||
switch len(parts) {
|
||||
case 2:
|
||||
ms, err := parsePerfData(string(parts[1]), ts)
|
||||
if err != nil {
|
||||
p.Log.Errorf("Failed to parse performance data: %s\n", err.Error())
|
||||
}
|
||||
metrics = append(metrics, ms...)
|
||||
fallthrough
|
||||
case 1:
|
||||
msg.Write(bytes.TrimSpace(parts[0]))
|
||||
default:
|
||||
return nil, errors.New("illegal output format")
|
||||
}
|
||||
|
||||
// Read long output.
|
||||
for s.Scan() {
|
||||
if bytes.Contains(s.Bytes(), []byte{'|'}) {
|
||||
parts := bytes.Split(s.Bytes(), []byte{'|'})
|
||||
if longmsg.Len() != 0 {
|
||||
longmsg.WriteByte('\n')
|
||||
}
|
||||
longmsg.Write(bytes.TrimSpace(parts[0]))
|
||||
|
||||
ms, err := parsePerfData(string(parts[1]), ts)
|
||||
if err != nil {
|
||||
p.Log.Errorf("Failed to parse performance data: %s\n", err.Error())
|
||||
}
|
||||
metrics = append(metrics, ms...)
|
||||
break
|
||||
}
|
||||
if longmsg.Len() != 0 {
|
||||
longmsg.WriteByte('\n')
|
||||
}
|
||||
longmsg.Write(bytes.TrimSpace(s.Bytes()))
|
||||
}
|
||||
|
||||
// Parse extra performance data.
|
||||
for s.Scan() {
|
||||
ms, err := parsePerfData(s.Text(), ts)
|
||||
if err != nil {
|
||||
p.Log.Errorf("Failed to parse performance data: %s\n", err.Error())
|
||||
}
|
||||
metrics = append(metrics, ms...)
|
||||
}
|
||||
|
||||
if s.Err() != nil {
|
||||
p.Log.Debugf("Unexpected io error: %s\n", s.Err())
|
||||
}
|
||||
|
||||
// Create nagios state.
|
||||
fields := map[string]interface{}{
|
||||
"service_output": msg.String(),
|
||||
}
|
||||
if longmsg.Len() != 0 {
|
||||
fields["long_service_output"] = longmsg.String()
|
||||
}
|
||||
|
||||
m := metric.New("nagios_state", nil, fields, ts)
|
||||
metrics = append(metrics, m)
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
func parsePerfData(perfdatas string, timestamp time.Time) ([]telegraf.Metric, error) {
|
||||
metrics := make([]telegraf.Metric, 0)
|
||||
|
||||
for _, unParsedPerf := range perfSplitRegExp.FindAllString(perfdatas, -1) {
|
||||
trimmedPerf := strings.TrimSpace(unParsedPerf)
|
||||
perf := nagiosRegExp.FindStringSubmatch(trimmedPerf)
|
||||
|
||||
// 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 := 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(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
|
||||
m := metric.New("nagios", tags, fields, timestamp)
|
||||
|
||||
// Add Metric
|
||||
metrics = append(metrics, m)
|
||||
}
|
||||
|
||||
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) (vmin, vmax float64, err error) {
|
||||
thresh := strings.Split(threshold, ":")
|
||||
switch len(thresh) {
|
||||
case 1:
|
||||
vmax, err = strconv.ParseFloat(thresh[0], 64)
|
||||
if err != nil {
|
||||
return 0, 0, ErrBadThresholdFormat
|
||||
}
|
||||
|
||||
return 0, vmax, nil
|
||||
case 2:
|
||||
if thresh[0] == "~" {
|
||||
vmin = MinFloat64
|
||||
} else {
|
||||
vmin, err = strconv.ParseFloat(thresh[0], 64)
|
||||
if err != nil {
|
||||
vmin = 0
|
||||
}
|
||||
}
|
||||
|
||||
if thresh[1] == "" {
|
||||
vmax = MaxFloat64
|
||||
} else {
|
||||
vmax, err = strconv.ParseFloat(thresh[1], 64)
|
||||
if err != nil {
|
||||
return 0, 0, ErrBadThresholdFormat
|
||||
}
|
||||
}
|
||||
default:
|
||||
return 0, 0, ErrBadThresholdFormat
|
||||
}
|
||||
|
||||
return vmin, vmax, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register parser
|
||||
parsers.Add("nagios",
|
||||
func(defaultMetricName string) telegraf.Parser {
|
||||
return &Parser{metricName: defaultMetricName}
|
||||
},
|
||||
)
|
||||
}
|
577
plugins/parsers/nagios/parser_test.go
Normal file
577
plugins/parsers/nagios/parser_test.go
Normal file
|
@ -0,0 +1,577 @@
|
|||
package nagios
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/metric"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestGetExitCode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
errF func() error
|
||||
expCode int
|
||||
expErr error
|
||||
}{
|
||||
{
|
||||
name: "nil error passed is ok",
|
||||
errF: func() error {
|
||||
return nil
|
||||
},
|
||||
expCode: 0,
|
||||
expErr: nil,
|
||||
},
|
||||
{
|
||||
name: "unexpected error type",
|
||||
errF: func() error {
|
||||
return errors.New("not *exec.ExitError")
|
||||
},
|
||||
expCode: 3,
|
||||
expErr: errors.New("not *exec.ExitError"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
e := tt.errF()
|
||||
code, err := getExitCode(e)
|
||||
|
||||
require.Equal(t, tt.expCode, code)
|
||||
require.Equal(t, tt.expErr, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type metricBuilder struct {
|
||||
name string
|
||||
tags map[string]string
|
||||
fields map[string]interface{}
|
||||
timestamp time.Time
|
||||
}
|
||||
|
||||
func mb() *metricBuilder {
|
||||
return &metricBuilder{}
|
||||
}
|
||||
|
||||
func (b *metricBuilder) n(v string) *metricBuilder {
|
||||
b.name = v
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *metricBuilder) f(k string, v interface{}) *metricBuilder {
|
||||
if b.fields == nil {
|
||||
b.fields = make(map[string]interface{})
|
||||
}
|
||||
b.fields[k] = v
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *metricBuilder) b() telegraf.Metric {
|
||||
m := metric.New(b.name, b.tags, b.fields, b.timestamp)
|
||||
return m
|
||||
}
|
||||
|
||||
// assertEqual asserts two slices to be equal. Note, that the order
|
||||
// of the entries matters.
|
||||
func assertEqual(t *testing.T, exp, actual []telegraf.Metric) {
|
||||
require.Len(t, actual, len(exp))
|
||||
for i := 0; i < len(exp); i++ {
|
||||
ok := testutil.MetricEqual(exp[i], actual[i])
|
||||
require.True(t, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTryAddState(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
runErrF func() error
|
||||
runErrMessage []byte
|
||||
metrics []telegraf.Metric
|
||||
assertF func(*testing.T, []telegraf.Metric)
|
||||
}{
|
||||
{
|
||||
name: "should append state=0 field to existing metric",
|
||||
runErrF: func() error {
|
||||
return nil
|
||||
},
|
||||
metrics: []telegraf.Metric{
|
||||
mb().
|
||||
n("nagios").
|
||||
f("perfdata", 0).b(),
|
||||
mb().
|
||||
n("nagios_state").
|
||||
f("service_output", "OK: system working").b(),
|
||||
},
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric) {
|
||||
exp := []telegraf.Metric{
|
||||
mb().
|
||||
n("nagios").
|
||||
f("perfdata", 0).b(),
|
||||
mb().
|
||||
n("nagios_state").
|
||||
f("service_output", "OK: system working").
|
||||
f("state", 0).b(),
|
||||
}
|
||||
assertEqual(t, exp, metrics)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should create 'nagios_state state=0' and same timestamp as others",
|
||||
runErrF: func() error {
|
||||
return nil
|
||||
},
|
||||
metrics: []telegraf.Metric{
|
||||
mb().
|
||||
n("nagios").
|
||||
f("perfdata", 0).b(),
|
||||
},
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric) {
|
||||
exp := []telegraf.Metric{
|
||||
mb().
|
||||
n("nagios").
|
||||
f("perfdata", 0).b(),
|
||||
mb().
|
||||
n("nagios_state").
|
||||
f("state", 0).b(),
|
||||
}
|
||||
assertEqual(t, exp, metrics)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should create 'nagios_state state=0' and recent timestamp",
|
||||
runErrF: func() error {
|
||||
return nil
|
||||
},
|
||||
metrics: make([]telegraf.Metric, 0),
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric) {
|
||||
require.Len(t, metrics, 1)
|
||||
m := metrics[0]
|
||||
require.Equal(t, "nagios_state", m.Name())
|
||||
s, ok := m.GetField("state")
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(0), s)
|
||||
require.WithinDuration(t, time.Now().UTC(), m.Time(), 10*time.Second)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return metrics with state unknown and thrown error is service_output",
|
||||
runErrF: func() error {
|
||||
return errors.New("non parsable error")
|
||||
},
|
||||
metrics: []telegraf.Metric{
|
||||
mb().
|
||||
n("nagios_state").b(),
|
||||
},
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric) {
|
||||
exp := []telegraf.Metric{
|
||||
mb().
|
||||
n("nagios_state").
|
||||
f("state", 3).
|
||||
f("service_output", "non parsable error").b(),
|
||||
}
|
||||
|
||||
assertEqual(t, exp, metrics)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return metrics with state unknown and service_output error from error message parameter",
|
||||
runErrF: func() error {
|
||||
return errors.New("")
|
||||
},
|
||||
runErrMessage: []byte("some error message"),
|
||||
metrics: []telegraf.Metric{
|
||||
mb().
|
||||
n("nagios_state").b(),
|
||||
},
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric) {
|
||||
exp := []telegraf.Metric{
|
||||
mb().
|
||||
n("nagios_state").
|
||||
f("state", 3).
|
||||
f("service_output", "some error message").b(),
|
||||
}
|
||||
assertEqual(t, exp, metrics)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
metrics := AddState(tt.runErrF(), tt.runErrMessage, tt.metrics)
|
||||
tt.assertF(t, metrics)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertNagiosState(t *testing.T, m telegraf.Metric, f map[string]interface{}) {
|
||||
require.Equal(t, map[string]string{}, m.Tags())
|
||||
require.Equal(t, f, m.Fields())
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
parser := Parser{
|
||||
metricName: "nagios_test",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
assertF func(*testing.T, []telegraf.Metric, error)
|
||||
}{
|
||||
{
|
||||
name: "valid output 1",
|
||||
input: `PING OK - Packet loss = 0%, RTA = 0.30 ms|rta=0.298000ms;4000.000000;6000.000000;0.000000 pl=0%;80;90;0;100
|
||||
This is a long output
|
||||
with three lines
|
||||
`,
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, metrics, 3)
|
||||
// rta
|
||||
require.Equal(t, map[string]string{
|
||||
"unit": "ms",
|
||||
"perfdata": "rta",
|
||||
}, metrics[0].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(0.298),
|
||||
"warning_lt": float64(0),
|
||||
"warning_gt": float64(4000),
|
||||
"critical_lt": float64(0),
|
||||
"critical_gt": float64(6000),
|
||||
"min": float64(0),
|
||||
}, metrics[0].Fields())
|
||||
|
||||
// pl
|
||||
require.Equal(t, map[string]string{
|
||||
"unit": "%",
|
||||
"perfdata": "pl",
|
||||
}, metrics[1].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(0),
|
||||
"warning_lt": float64(0),
|
||||
"warning_gt": float64(80),
|
||||
"critical_lt": float64(0),
|
||||
"critical_gt": float64(90),
|
||||
"min": float64(0),
|
||||
"max": float64(100),
|
||||
}, metrics[1].Fields())
|
||||
|
||||
assertNagiosState(t, metrics[2], map[string]interface{}{
|
||||
"service_output": "PING OK - Packet loss = 0%, RTA = 0.30 ms",
|
||||
"long_service_output": "This is a long output\nwith three lines",
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid output 2",
|
||||
input: "TCP OK - 0.008 second response time on port 80|time=0.008457s;;;0.000000;10.000000",
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, metrics, 2)
|
||||
// time
|
||||
require.Equal(t, map[string]string{
|
||||
"unit": "s",
|
||||
"perfdata": "time",
|
||||
}, metrics[0].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(0.008457),
|
||||
"min": float64(0),
|
||||
"max": float64(10),
|
||||
}, metrics[0].Fields())
|
||||
|
||||
assertNagiosState(t, metrics[1], map[string]interface{}{
|
||||
"service_output": "TCP OK - 0.008 second response time on port 80",
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid output 3",
|
||||
input: "TCP OK - 0.008 second response time on port 80|time=0.008457",
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, metrics, 2)
|
||||
// time
|
||||
require.Equal(t, map[string]string{
|
||||
"perfdata": "time",
|
||||
}, metrics[0].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(0.008457),
|
||||
}, metrics[0].Fields())
|
||||
|
||||
assertNagiosState(t, metrics[1], map[string]interface{}{
|
||||
"service_output": "TCP OK - 0.008 second response time on port 80",
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid output 4",
|
||||
input: "OK: Load average: 0.00, 0.01, 0.05 | 'load1'=0.00;~:4;@0:6;0; 'load5'=0.01;3;0:5;0; 'load15'=0.05;0:2;0:4;0;",
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, metrics, 4)
|
||||
// load1
|
||||
require.Equal(t, map[string]string{
|
||||
"perfdata": "load1",
|
||||
}, metrics[0].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(0.00),
|
||||
"warning_lt": MinFloat64,
|
||||
"warning_gt": float64(4),
|
||||
"critical_le": float64(0),
|
||||
"critical_ge": float64(6),
|
||||
"min": float64(0),
|
||||
}, metrics[0].Fields())
|
||||
|
||||
// load5
|
||||
require.Equal(t, map[string]string{
|
||||
"perfdata": "load5",
|
||||
}, metrics[1].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(0.01),
|
||||
"warning_gt": float64(3),
|
||||
"warning_lt": float64(0),
|
||||
"critical_lt": float64(0),
|
||||
"critical_gt": float64(5),
|
||||
"min": float64(0),
|
||||
}, metrics[1].Fields())
|
||||
|
||||
// load15
|
||||
require.Equal(t, map[string]string{
|
||||
"perfdata": "load15",
|
||||
}, metrics[2].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(0.05),
|
||||
"warning_lt": float64(0),
|
||||
"warning_gt": float64(2),
|
||||
"critical_lt": float64(0),
|
||||
"critical_gt": float64(4),
|
||||
"min": float64(0),
|
||||
}, metrics[2].Fields())
|
||||
|
||||
assertNagiosState(t, metrics[3], map[string]interface{}{
|
||||
"service_output": "OK: Load average: 0.00, 0.01, 0.05",
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no perf data",
|
||||
input: "PING OK - Packet loss = 0%, RTA = 0.30 ms",
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, metrics, 1)
|
||||
|
||||
assertNagiosState(t, metrics[0], map[string]interface{}{
|
||||
"service_output": "PING OK - Packet loss = 0%, RTA = 0.30 ms",
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "malformed perf data",
|
||||
input: "PING OK - Packet loss = 0%, RTA = 0.30 ms| =3;;;; dgasdg =;;;; sff=;;;;",
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, metrics, 1)
|
||||
|
||||
assertNagiosState(t, metrics[0], map[string]interface{}{
|
||||
"service_output": "PING OK - Packet loss = 0%, RTA = 0.30 ms",
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/pluginapi.html",
|
||||
input: `DISK OK - free space: / 3326 MB (56%); | /=2643MB;5948;5958;0;5968
|
||||
/ 15272 MB (77%);
|
||||
/boot 68 MB (69%);
|
||||
/home 69357 MB (27%);
|
||||
/var/log 819 MB (84%); | /boot=68MB;88;93;0;98
|
||||
/home=69357MB;253404;253409;0;253414
|
||||
/var/log=818MB;970;975;0;980
|
||||
`,
|
||||
assertF: func(t *testing.T, metrics []telegraf.Metric, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, metrics, 5)
|
||||
// /=2643MB;5948;5958;0;5968
|
||||
require.Equal(t, map[string]string{
|
||||
"unit": "MB",
|
||||
"perfdata": "/",
|
||||
}, metrics[0].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(2643),
|
||||
"warning_lt": float64(0),
|
||||
"warning_gt": float64(5948),
|
||||
"critical_lt": float64(0),
|
||||
"critical_gt": float64(5958),
|
||||
"min": float64(0),
|
||||
"max": float64(5968),
|
||||
}, metrics[0].Fields())
|
||||
|
||||
// /boot=68MB;88;93;0;98
|
||||
require.Equal(t, map[string]string{
|
||||
"unit": "MB",
|
||||
"perfdata": "/boot",
|
||||
}, metrics[1].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(68),
|
||||
"warning_lt": float64(0),
|
||||
"warning_gt": float64(88),
|
||||
"critical_lt": float64(0),
|
||||
"critical_gt": float64(93),
|
||||
"min": float64(0),
|
||||
"max": float64(98),
|
||||
}, metrics[1].Fields())
|
||||
|
||||
// /home=69357MB;253404;253409;0;253414
|
||||
require.Equal(t, map[string]string{
|
||||
"unit": "MB",
|
||||
"perfdata": "/home",
|
||||
}, metrics[2].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(69357),
|
||||
"warning_lt": float64(0),
|
||||
"warning_gt": float64(253404),
|
||||
"critical_lt": float64(0),
|
||||
"critical_gt": float64(253409),
|
||||
"min": float64(0),
|
||||
"max": float64(253414),
|
||||
}, metrics[2].Fields())
|
||||
|
||||
// /var/log=818MB;970;975;0;980
|
||||
require.Equal(t, map[string]string{
|
||||
"unit": "MB",
|
||||
"perfdata": "/var/log",
|
||||
}, metrics[3].Tags())
|
||||
require.Equal(t, map[string]interface{}{
|
||||
"value": float64(818),
|
||||
"warning_lt": float64(0),
|
||||
"warning_gt": float64(970),
|
||||
"critical_lt": float64(0),
|
||||
"critical_gt": float64(975),
|
||||
"min": float64(0),
|
||||
"max": float64(980),
|
||||
}, metrics[3].Fields())
|
||||
|
||||
assertNagiosState(t, metrics[4], map[string]interface{}{
|
||||
"service_output": "DISK OK - free space: / 3326 MB (56%);",
|
||||
"long_service_output": "/ 15272 MB (77%);\n/boot 68 MB (69%);\n/home 69357 MB (27%);\n/var/log 819 MB (84%);",
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
metrics, err := parser.Parse([]byte(tt.input))
|
||||
tt.assertF(t, metrics, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseThreshold(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
eMin float64
|
||||
eMax float64
|
||||
eErr error
|
||||
}{
|
||||
{
|
||||
input: "10",
|
||||
eMin: 0,
|
||||
eMax: 10,
|
||||
eErr: nil,
|
||||
},
|
||||
{
|
||||
input: "10:",
|
||||
eMin: 10,
|
||||
eMax: MaxFloat64,
|
||||
eErr: nil,
|
||||
},
|
||||
{
|
||||
input: "~:10",
|
||||
eMin: MinFloat64,
|
||||
eMax: 10,
|
||||
eErr: nil,
|
||||
},
|
||||
{
|
||||
input: "10:20",
|
||||
eMin: 10,
|
||||
eMax: 20,
|
||||
eErr: nil,
|
||||
},
|
||||
{
|
||||
input: "10:20",
|
||||
eMin: 10,
|
||||
eMax: 20,
|
||||
eErr: nil,
|
||||
},
|
||||
{
|
||||
input: "10:20:30",
|
||||
eMin: 0,
|
||||
eMax: 0,
|
||||
eErr: ErrBadThresholdFormat,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range tests {
|
||||
vmin, vmax, err := parseThreshold(tests[i].input)
|
||||
require.InDelta(t, tests[i].eMin, vmin, testutil.DefaultDelta)
|
||||
require.InDelta(t, tests[i].eMax, vmax, testutil.DefaultDelta)
|
||||
require.Equal(t, tests[i].eErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
const benchmarkData = `DISK OK - free space: / 3326 MB (56%); | /=2643MB;5948;5958;0;5968
|
||||
/ 15272 MB (77%);
|
||||
/boot 68 MB (69%);
|
||||
`
|
||||
|
||||
func TestBenchmarkData(t *testing.T) {
|
||||
plugin := &Parser{}
|
||||
|
||||
expected := []telegraf.Metric{
|
||||
metric.New(
|
||||
"nagios",
|
||||
map[string]string{
|
||||
"perfdata": "/",
|
||||
"unit": "MB",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"critical_gt": 5958.0,
|
||||
"critical_lt": 0.0,
|
||||
"min": 0.0,
|
||||
"max": 5968.0,
|
||||
"value": 2643.0,
|
||||
"warning_gt": 5948.0,
|
||||
"warning_lt": 0.0,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
metric.New(
|
||||
"nagios_state",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"long_service_output": "/ 15272 MB (77%);\n/boot 68 MB (69%);",
|
||||
"service_output": "DISK OK - free space: / 3326 MB (56%);",
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
}
|
||||
|
||||
actual, err := plugin.Parse([]byte(benchmarkData))
|
||||
require.NoError(t, err)
|
||||
testutil.RequireMetricsEqual(t, expected, actual, testutil.IgnoreTime(), testutil.SortMetrics())
|
||||
}
|
||||
|
||||
func BenchmarkParsing(b *testing.B) {
|
||||
plugin := &Parser{}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
//nolint:errcheck // Benchmarking so skip the error check to avoid the unnecessary operations
|
||||
plugin.Parse([]byte(benchmarkData))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue