2022-05-24 21:49:47 +08:00
|
|
|
//go:generate ../../../tools/readme_config_includer/generator
|
2016-01-27 08:12:54 +08:00
|
|
|
package net_response
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
2022-05-24 21:49:47 +08:00
|
|
|
_ "embed"
|
2016-01-27 08:12:54 +08:00
|
|
|
"errors"
|
2023-02-22 18:36:58 +08:00
|
|
|
"fmt"
|
2016-01-27 08:12:54 +08:00
|
|
|
"net"
|
|
|
|
|
"net/textproto"
|
|
|
|
|
"regexp"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/influxdata/telegraf"
|
2021-04-10 01:15:04 +08:00
|
|
|
"github.com/influxdata/telegraf/config"
|
2023-02-22 18:36:58 +08:00
|
|
|
"github.com/influxdata/telegraf/internal/choice"
|
2016-01-27 08:12:54 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/inputs"
|
|
|
|
|
)
|
|
|
|
|
|
2022-05-24 21:49:47 +08:00
|
|
|
//go:embed sample.conf
|
|
|
|
|
var sampleConfig string
|
|
|
|
|
|
2018-05-09 07:04:28 +08:00
|
|
|
type ResultType uint64
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
Success ResultType = 0
|
2021-11-02 22:42:22 +08:00
|
|
|
Timeout ResultType = 1
|
|
|
|
|
ConnectionFailed ResultType = 2
|
|
|
|
|
ReadFailed ResultType = 3
|
|
|
|
|
StringMismatch ResultType = 4
|
2018-05-09 07:04:28 +08:00
|
|
|
)
|
|
|
|
|
|
2018-05-09 07:07:15 +08:00
|
|
|
// NetResponse struct
|
2016-01-27 08:12:54 +08:00
|
|
|
type NetResponse struct {
|
|
|
|
|
Address string
|
2021-04-10 01:15:04 +08:00
|
|
|
Timeout config.Duration
|
|
|
|
|
ReadTimeout config.Duration
|
2016-01-27 08:12:54 +08:00
|
|
|
Send string
|
|
|
|
|
Expect string
|
|
|
|
|
Protocol string
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-24 21:49:47 +08:00
|
|
|
func (*NetResponse) SampleConfig() string {
|
|
|
|
|
return sampleConfig
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-09 07:07:15 +08:00
|
|
|
// TCPGather will execute if there are TCP tests defined in the configuration.
|
|
|
|
|
// It will return a map[string]interface{} for fields and a map[string]string for tags
|
2021-04-09 00:43:39 +08:00
|
|
|
func (n *NetResponse) TCPGather() (map[string]string, map[string]interface{}, error) {
|
2018-05-09 07:07:15 +08:00
|
|
|
// Prepare returns
|
2021-04-09 00:43:39 +08:00
|
|
|
tags := make(map[string]string)
|
|
|
|
|
fields := make(map[string]interface{})
|
2016-01-27 08:12:54 +08:00
|
|
|
// Start Timer
|
|
|
|
|
start := time.Now()
|
|
|
|
|
// Connecting
|
2021-04-10 01:15:04 +08:00
|
|
|
conn, err := net.DialTimeout("tcp", n.Address, time.Duration(n.Timeout))
|
2016-01-27 08:12:54 +08:00
|
|
|
// Stop timer
|
|
|
|
|
responseTime := time.Since(start).Seconds()
|
|
|
|
|
// Handle error
|
|
|
|
|
if err != nil {
|
2023-03-02 05:19:38 +08:00
|
|
|
var e net.Error
|
|
|
|
|
if errors.As(err, &e) && e.Timeout() {
|
2018-05-09 07:04:28 +08:00
|
|
|
setResult(Timeout, fields, tags, n.Expect)
|
2017-07-15 01:43:36 +08:00
|
|
|
} else {
|
2018-05-09 07:04:28 +08:00
|
|
|
setResult(ConnectionFailed, fields, tags, n.Expect)
|
2017-07-15 01:43:36 +08:00
|
|
|
}
|
2021-04-09 00:43:39 +08:00
|
|
|
return tags, fields, nil
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
|
|
|
|
defer conn.Close()
|
|
|
|
|
// Send string if needed
|
2016-05-23 20:33:43 +08:00
|
|
|
if n.Send != "" {
|
|
|
|
|
msg := []byte(n.Send)
|
2021-04-09 00:43:39 +08:00
|
|
|
if _, gerr := conn.Write(msg); gerr != nil {
|
|
|
|
|
return nil, nil, gerr
|
|
|
|
|
}
|
2016-01-27 08:12:54 +08:00
|
|
|
// Stop timer
|
|
|
|
|
responseTime = time.Since(start).Seconds()
|
|
|
|
|
}
|
|
|
|
|
// Read string if needed
|
2016-05-23 20:33:43 +08:00
|
|
|
if n.Expect != "" {
|
2016-01-27 08:12:54 +08:00
|
|
|
// Set read timeout
|
2021-04-10 01:15:04 +08:00
|
|
|
if gerr := conn.SetReadDeadline(time.Now().Add(time.Duration(n.ReadTimeout))); gerr != nil {
|
2021-04-09 00:43:39 +08:00
|
|
|
return nil, nil, gerr
|
|
|
|
|
}
|
2016-01-27 08:12:54 +08:00
|
|
|
// Prepare reader
|
|
|
|
|
reader := bufio.NewReader(conn)
|
|
|
|
|
tp := textproto.NewReader(reader)
|
|
|
|
|
// Read
|
|
|
|
|
data, err := tp.ReadLine()
|
|
|
|
|
// Stop timer
|
|
|
|
|
responseTime = time.Since(start).Seconds()
|
|
|
|
|
// Handle error
|
|
|
|
|
if err != nil {
|
2018-05-09 07:04:28 +08:00
|
|
|
setResult(ReadFailed, fields, tags, n.Expect)
|
2016-01-27 08:12:54 +08:00
|
|
|
} else {
|
|
|
|
|
// Looking for string in answer
|
2021-11-02 22:42:22 +08:00
|
|
|
regEx := regexp.MustCompile(`.*` + n.Expect + `.*`)
|
|
|
|
|
find := regEx.FindString(data)
|
2016-01-27 08:12:54 +08:00
|
|
|
if find != "" {
|
2018-05-09 07:04:28 +08:00
|
|
|
setResult(Success, fields, tags, n.Expect)
|
2016-01-27 08:12:54 +08:00
|
|
|
} else {
|
2018-05-09 07:04:28 +08:00
|
|
|
setResult(StringMismatch, fields, tags, n.Expect)
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
|
|
|
|
}
|
2017-07-15 01:43:36 +08:00
|
|
|
} else {
|
2018-05-09 07:04:28 +08:00
|
|
|
setResult(Success, fields, tags, n.Expect)
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
|
|
|
|
fields["response_time"] = responseTime
|
2021-04-09 00:43:39 +08:00
|
|
|
return tags, fields, nil
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-09 07:07:15 +08:00
|
|
|
// UDPGather will execute if there are UDP tests defined in the configuration.
|
|
|
|
|
// It will return a map[string]interface{} for fields and a map[string]string for tags
|
2021-04-09 00:43:39 +08:00
|
|
|
func (n *NetResponse) UDPGather() (map[string]string, map[string]interface{}, error) {
|
2018-05-09 07:07:15 +08:00
|
|
|
// Prepare returns
|
2021-04-09 00:43:39 +08:00
|
|
|
tags := make(map[string]string)
|
|
|
|
|
fields := make(map[string]interface{})
|
2016-01-27 08:12:54 +08:00
|
|
|
// Start Timer
|
|
|
|
|
start := time.Now()
|
|
|
|
|
// Resolving
|
2016-05-23 20:33:43 +08:00
|
|
|
udpAddr, err := net.ResolveUDPAddr("udp", n.Address)
|
2020-10-08 23:20:35 +08:00
|
|
|
// Handle error
|
|
|
|
|
if err != nil {
|
|
|
|
|
setResult(ConnectionFailed, fields, tags, n.Expect)
|
2023-01-26 18:27:32 +08:00
|
|
|
return tags, fields, nil //nolint:nilerr // error encoded in result
|
2020-10-08 23:20:35 +08:00
|
|
|
}
|
2016-01-27 08:12:54 +08:00
|
|
|
// Connecting
|
2019-01-03 05:53:58 +08:00
|
|
|
conn, err := net.DialUDP("udp", nil, udpAddr)
|
2016-01-27 08:12:54 +08:00
|
|
|
// Handle error
|
|
|
|
|
if err != nil {
|
2018-05-09 07:04:28 +08:00
|
|
|
setResult(ConnectionFailed, fields, tags, n.Expect)
|
2023-01-26 18:27:32 +08:00
|
|
|
return tags, fields, nil //nolint:nilerr // error encoded in result
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
2017-07-15 01:43:36 +08:00
|
|
|
defer conn.Close()
|
2016-01-27 08:12:54 +08:00
|
|
|
// Send string
|
2016-05-23 20:33:43 +08:00
|
|
|
msg := []byte(n.Send)
|
2021-04-09 00:43:39 +08:00
|
|
|
if _, gerr := conn.Write(msg); gerr != nil {
|
|
|
|
|
return nil, nil, gerr
|
|
|
|
|
}
|
2016-01-27 08:12:54 +08:00
|
|
|
// Read string
|
|
|
|
|
// Set read timeout
|
2021-04-10 01:15:04 +08:00
|
|
|
if gerr := conn.SetReadDeadline(time.Now().Add(time.Duration(n.ReadTimeout))); gerr != nil {
|
2021-04-09 00:43:39 +08:00
|
|
|
return nil, nil, gerr
|
|
|
|
|
}
|
2016-01-27 08:12:54 +08:00
|
|
|
// Read
|
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
|
_, _, err = conn.ReadFromUDP(buf)
|
|
|
|
|
// Stop timer
|
|
|
|
|
responseTime := time.Since(start).Seconds()
|
|
|
|
|
// Handle error
|
|
|
|
|
if err != nil {
|
2018-05-09 07:04:28 +08:00
|
|
|
setResult(ReadFailed, fields, tags, n.Expect)
|
2023-01-26 18:27:32 +08:00
|
|
|
return tags, fields, nil //nolint:nilerr // error encoded in result
|
2018-05-09 07:07:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Looking for string in answer
|
2021-11-02 22:42:22 +08:00
|
|
|
regEx := regexp.MustCompile(`.*` + n.Expect + `.*`)
|
|
|
|
|
find := regEx.FindString(string(buf))
|
2018-05-09 07:07:15 +08:00
|
|
|
if find != "" {
|
2018-05-09 07:04:28 +08:00
|
|
|
setResult(Success, fields, tags, n.Expect)
|
2016-01-27 08:12:54 +08:00
|
|
|
} else {
|
2018-05-09 07:04:28 +08:00
|
|
|
setResult(StringMismatch, fields, tags, n.Expect)
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
2018-05-09 07:07:15 +08:00
|
|
|
|
2016-01-27 08:12:54 +08:00
|
|
|
fields["response_time"] = responseTime
|
2018-05-09 07:07:15 +08:00
|
|
|
|
2021-04-09 00:43:39 +08:00
|
|
|
return tags, fields, nil
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
|
|
|
|
|
2023-02-22 18:36:58 +08:00
|
|
|
// Init performs one time setup of the plugin and returns an error if the
|
|
|
|
|
// configuration is invalid.
|
|
|
|
|
func (n *NetResponse) Init() error {
|
2016-01-27 08:12:54 +08:00
|
|
|
// Set default values
|
2021-04-10 01:15:04 +08:00
|
|
|
if n.Timeout == 0 {
|
|
|
|
|
n.Timeout = config.Duration(time.Second)
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
2021-04-10 01:15:04 +08:00
|
|
|
if n.ReadTimeout == 0 {
|
|
|
|
|
n.ReadTimeout = config.Duration(time.Second)
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
|
|
|
|
// Check send and expected string
|
2016-05-23 20:33:43 +08:00
|
|
|
if n.Protocol == "udp" && n.Send == "" {
|
2021-03-24 23:27:46 +08:00
|
|
|
return errors.New("send string cannot be empty")
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
2016-05-23 20:33:43 +08:00
|
|
|
if n.Protocol == "udp" && n.Expect == "" {
|
2021-03-24 23:27:46 +08:00
|
|
|
return errors.New("expected string cannot be empty")
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
|
|
|
|
// Prepare host and port
|
2016-05-23 20:33:43 +08:00
|
|
|
host, port, err := net.SplitHostPort(n.Address)
|
2016-01-27 08:12:54 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if host == "" {
|
2016-05-23 20:33:43 +08:00
|
|
|
n.Address = "localhost:" + port
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
|
|
|
|
if port == "" {
|
2023-02-22 18:36:58 +08:00
|
|
|
return errors.New("bad port in config option address")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := choice.Check(n.Protocol, []string{"tcp", "udp"}); err != nil {
|
|
|
|
|
return fmt.Errorf("config option protocol: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Gather is called by telegraf when the plugin is executed on its interval.
|
|
|
|
|
// It will call either UDPGather or TCPGather based on the configuration and
|
|
|
|
|
// also fill an Accumulator that is supplied.
|
|
|
|
|
func (n *NetResponse) Gather(acc telegraf.Accumulator) error {
|
|
|
|
|
// Prepare host and port
|
|
|
|
|
host, port, err := net.SplitHostPort(n.Address)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
2023-02-22 18:36:58 +08:00
|
|
|
|
2016-01-27 08:12:54 +08:00
|
|
|
// Prepare data
|
2016-03-11 03:41:03 +08:00
|
|
|
tags := map[string]string{"server": host, "port": port}
|
2016-01-27 08:12:54 +08:00
|
|
|
var fields map[string]interface{}
|
2018-05-09 07:07:15 +08:00
|
|
|
var returnTags map[string]string
|
2021-11-02 22:42:22 +08:00
|
|
|
|
2016-01-27 08:12:54 +08:00
|
|
|
// Gather data
|
2021-11-02 22:42:22 +08:00
|
|
|
switch n.Protocol {
|
|
|
|
|
case "tcp":
|
2021-04-09 00:43:39 +08:00
|
|
|
returnTags, fields, err = n.TCPGather()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2016-01-27 08:12:54 +08:00
|
|
|
tags["protocol"] = "tcp"
|
2021-11-02 22:42:22 +08:00
|
|
|
case "udp":
|
2021-04-09 00:43:39 +08:00
|
|
|
returnTags, fields, err = n.UDPGather()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2016-01-27 08:12:54 +08:00
|
|
|
tags["protocol"] = "udp"
|
|
|
|
|
}
|
2021-11-02 22:42:22 +08:00
|
|
|
|
2018-05-09 07:07:15 +08:00
|
|
|
// Merge the tags
|
|
|
|
|
for k, v := range returnTags {
|
|
|
|
|
tags[k] = v
|
2016-01-27 08:12:54 +08:00
|
|
|
}
|
|
|
|
|
// Add metrics
|
|
|
|
|
acc.AddFields("net_response", fields, tags)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-09 07:04:28 +08:00
|
|
|
func setResult(result ResultType, fields map[string]interface{}, tags map[string]string, expect string) {
|
|
|
|
|
var tag string
|
|
|
|
|
switch result {
|
|
|
|
|
case Success:
|
|
|
|
|
tag = "success"
|
|
|
|
|
case Timeout:
|
|
|
|
|
tag = "timeout"
|
|
|
|
|
case ConnectionFailed:
|
|
|
|
|
tag = "connection_failed"
|
|
|
|
|
case ReadFailed:
|
|
|
|
|
tag = "read_failed"
|
|
|
|
|
case StringMismatch:
|
|
|
|
|
tag = "string_mismatch"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tags["result"] = tag
|
|
|
|
|
fields["result_code"] = uint64(result)
|
|
|
|
|
|
|
|
|
|
// deprecated in 1.7; use result tag
|
|
|
|
|
fields["result_type"] = tag
|
|
|
|
|
|
|
|
|
|
// deprecated in 1.4; use result tag
|
|
|
|
|
if expect != "" {
|
|
|
|
|
fields["string_found"] = result == Success
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-27 08:12:54 +08:00
|
|
|
func init() {
|
|
|
|
|
inputs.Add("net_response", func() telegraf.Input {
|
|
|
|
|
return &NetResponse{}
|
|
|
|
|
})
|
|
|
|
|
}
|