215 lines
5.2 KiB
Go
215 lines
5.2 KiB
Go
package net_response
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"net"
|
|
"net/textproto"
|
|
"regexp"
|
|
"time"
|
|
|
|
"github.com/influxdata/telegraf"
|
|
"github.com/influxdata/telegraf/internal"
|
|
"github.com/influxdata/telegraf/plugins/inputs"
|
|
)
|
|
|
|
// NetResponses struct
|
|
type NetResponse struct {
|
|
Address string
|
|
Timeout internal.Duration
|
|
ReadTimeout internal.Duration
|
|
Send string
|
|
Expect string
|
|
Protocol string
|
|
}
|
|
|
|
func (_ *NetResponse) Description() string {
|
|
return "TCP or UDP 'ping' given url and collect response time in seconds"
|
|
}
|
|
|
|
var sampleConfig = `
|
|
## Protocol, must be "tcp" or "udp"
|
|
## NOTE: because the "udp" protocol does not respond to requests, it requires
|
|
## a send/expect string pair (see below).
|
|
protocol = "tcp"
|
|
## Server address (default localhost)
|
|
address = "localhost:80"
|
|
## Set timeout
|
|
timeout = "1s"
|
|
|
|
## Set read timeout (only used if expecting a response)
|
|
read_timeout = "1s"
|
|
|
|
## The following options are required for UDP checks. For TCP, they are
|
|
## optional. The plugin will send the given string to the server and then
|
|
## expect to receive the given 'expect' string back.
|
|
## string sent to the server
|
|
# send = "ssh"
|
|
## expected string in answer
|
|
# expect = "ssh"
|
|
`
|
|
|
|
func (_ *NetResponse) SampleConfig() string {
|
|
return sampleConfig
|
|
}
|
|
|
|
func (n *NetResponse) TcpGather() (map[string]interface{}, error) {
|
|
// Prepare fields
|
|
fields := make(map[string]interface{})
|
|
// Start Timer
|
|
start := time.Now()
|
|
// Connecting
|
|
conn, err := net.DialTimeout("tcp", n.Address, n.Timeout.Duration)
|
|
// Stop timer
|
|
responseTime := time.Since(start).Seconds()
|
|
// Handle error
|
|
if err != nil {
|
|
if e, ok := err.(net.Error); ok && e.Timeout() {
|
|
fields["result_type"] = "timeout"
|
|
} else {
|
|
fields["result_type"] = "connection_failed"
|
|
}
|
|
return fields, nil
|
|
}
|
|
defer conn.Close()
|
|
// Send string if needed
|
|
if n.Send != "" {
|
|
msg := []byte(n.Send)
|
|
conn.Write(msg)
|
|
// Stop timer
|
|
responseTime = time.Since(start).Seconds()
|
|
}
|
|
// Read string if needed
|
|
if n.Expect != "" {
|
|
// Set read timeout
|
|
conn.SetReadDeadline(time.Now().Add(n.ReadTimeout.Duration))
|
|
// 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 {
|
|
fields["string_found"] = false
|
|
fields["result_type"] = "read_failed"
|
|
} else {
|
|
// Looking for string in answer
|
|
RegEx := regexp.MustCompile(`.*` + n.Expect + `.*`)
|
|
find := RegEx.FindString(string(data))
|
|
if find != "" {
|
|
fields["result_type"] = "success"
|
|
fields["string_found"] = true
|
|
} else {
|
|
fields["result_type"] = "string_mismatch"
|
|
fields["string_found"] = false
|
|
}
|
|
}
|
|
} else {
|
|
fields["result_type"] = "success"
|
|
}
|
|
fields["response_time"] = responseTime
|
|
return fields, nil
|
|
}
|
|
|
|
func (n *NetResponse) UdpGather() (map[string]interface{}, error) {
|
|
// Prepare fields
|
|
fields := make(map[string]interface{})
|
|
// Start Timer
|
|
start := time.Now()
|
|
// Resolving
|
|
udpAddr, err := net.ResolveUDPAddr("udp", n.Address)
|
|
LocalAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
|
|
// Connecting
|
|
conn, err := net.DialUDP("udp", LocalAddr, udpAddr)
|
|
// Handle error
|
|
if err != nil {
|
|
fields["result_type"] = "connection_failed"
|
|
return fields, nil
|
|
}
|
|
defer conn.Close()
|
|
// Send string
|
|
msg := []byte(n.Send)
|
|
conn.Write(msg)
|
|
// Read string
|
|
// Set read timeout
|
|
conn.SetReadDeadline(time.Now().Add(n.ReadTimeout.Duration))
|
|
// Read
|
|
buf := make([]byte, 1024)
|
|
_, _, err = conn.ReadFromUDP(buf)
|
|
// Stop timer
|
|
responseTime := time.Since(start).Seconds()
|
|
// Handle error
|
|
if err != nil {
|
|
fields["result_type"] = "read_failed"
|
|
return fields, nil
|
|
} else {
|
|
// Looking for string in answer
|
|
RegEx := regexp.MustCompile(`.*` + n.Expect + `.*`)
|
|
find := RegEx.FindString(string(buf))
|
|
if find != "" {
|
|
fields["result_type"] = "success"
|
|
fields["string_found"] = true
|
|
} else {
|
|
fields["result_type"] = "string_mismatch"
|
|
fields["string_found"] = false
|
|
}
|
|
}
|
|
fields["response_time"] = responseTime
|
|
return fields, nil
|
|
}
|
|
|
|
func (n *NetResponse) Gather(acc telegraf.Accumulator) error {
|
|
// Set default values
|
|
if n.Timeout.Duration == 0 {
|
|
n.Timeout.Duration = time.Second
|
|
}
|
|
if n.ReadTimeout.Duration == 0 {
|
|
n.ReadTimeout.Duration = time.Second
|
|
}
|
|
// Check send and expected string
|
|
if n.Protocol == "udp" && n.Send == "" {
|
|
return errors.New("Send string cannot be empty")
|
|
}
|
|
if n.Protocol == "udp" && n.Expect == "" {
|
|
return errors.New("Expected string cannot be empty")
|
|
}
|
|
// Prepare host and port
|
|
host, port, err := net.SplitHostPort(n.Address)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if host == "" {
|
|
n.Address = "localhost:" + port
|
|
}
|
|
if port == "" {
|
|
return errors.New("Bad port")
|
|
}
|
|
// Prepare data
|
|
tags := map[string]string{"server": host, "port": port}
|
|
var fields map[string]interface{}
|
|
// Gather data
|
|
if n.Protocol == "tcp" {
|
|
fields, err = n.TcpGather()
|
|
tags["protocol"] = "tcp"
|
|
} else if n.Protocol == "udp" {
|
|
fields, err = n.UdpGather()
|
|
tags["protocol"] = "udp"
|
|
} else {
|
|
return errors.New("Bad protocol")
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Add metrics
|
|
acc.AddFields("net_response", fields, tags)
|
|
return nil
|
|
}
|
|
|
|
func init() {
|
|
inputs.Add("net_response", func() telegraf.Input {
|
|
return &NetResponse{}
|
|
})
|
|
}
|