Add tag/integer pair for result to net_response (#3455)

This commit is contained in:
Randy Coburn 2018-05-09 00:07:15 +01:00 committed by Daniel Nelson
parent 1d9488bd42
commit 54a0532604
3 changed files with 171 additions and 56 deletions

View File

@ -59,7 +59,9 @@ It can also check response text.
- net_response - net_response
- response_time (float, seconds) - response_time (float, seconds)
- result_type (string) # success, timeout, connection_failed, read_failed, string_mismatch - success (int) # success 0, failure 1
- result_code (int) # success = 0, Failure = 1
- [**DEPRECATED**] result_type (string) # success, timeout, connection_failed, read_failed, string_mismatch
- [**DEPRECATED**] string_found (boolean) - [**DEPRECATED**] string_found (boolean)
### Tags: ### Tags:
@ -68,16 +70,16 @@ It can also check response text.
- server - server
- port - port
- protocol - protocol
- result_text (string) # This will contain the text from the now deprecated result_type
### Example Output: ### Example Output:
``` ```
$ ./telegraf --config telegraf.conf --input-filter net_response --test net_response,server=influxdata.com,port=8080,protocol=tcp,host=localhost,result_text="timeout" result_code=1i,result_type="timeout" 1499310361000000000
net_response,server=influxdata.com,port=8080,protocol=tcp,host=localhost result_type="timeout" 1499310361000000000 net_response,server=influxdata.com,port=443,protocol=tcp,host=localhost,result_text="success" result_code=0i,result_type="success",response_time=0.088703864 1499310361000000000
net_response,server=influxdata.com,port=443,protocol=tcp,host=localhost result_type="success",response_time=0.088703864 1499310361000000000 net_response,protocol=tcp,host=localhost,server=this.domain.does.not.exist,port=443,result_text="connection_failed" result_code=1i,result_type="connection_failed" 1499310361000000000
net_response,protocol=tcp,host=localhost,server=this.domain.does.not.exist,port=443 result_type="connection_failed" 1499310361000000000 net_response,protocol=udp,host=localhost,server=influxdata.com,port=8080,result_text="read_failed" result_code=1i,result_type="read_failed" 1499310362000000000
net_response,protocol=udp,host=localhost,server=influxdata.com,port=8080 result_type="read_failed" 1499310362000000000 net_response,port=31338,protocol=udp,host=localhost,server=localhost,result_text="string_mismatch" result_code=1i,result_type="string_mismatch",string_found=false,response_time=0.00242682 1499310362000000000
net_response,port=31338,protocol=udp,host=localhost,server=localhost result_type="string_mismatch",string_found=false,response_time=0.00242682 1499310362000000000 net_response,protocol=udp,host=localhost,server=localhost,port=31338,result_text="success" response_time=0.001128598,result_code=0i,result_type="success",string_found=true 1499310362000000000
net_response,protocol=udp,host=localhost,server=localhost,port=31338 response_time=0.001128598,result_type="success",string_found=true 1499310362000000000 net_response,server=this.domain.does.not.exist,port=443,protocol=udp,host=localhost,result_text="connection_failed" result_code=1i,result_type="connection_failed" 1499310362000000000
net_response,server=this.domain.does.not.exist,port=443,protocol=udp,host=localhost result_type="connection_failed" 1499310362000000000
``` ```

View File

@ -13,7 +13,7 @@ import (
"github.com/influxdata/telegraf/plugins/inputs" "github.com/influxdata/telegraf/plugins/inputs"
) )
// NetResponses struct // NetResponse struct
type NetResponse struct { type NetResponse struct {
Address string Address string
Timeout internal.Duration Timeout internal.Duration
@ -23,8 +23,11 @@ type NetResponse struct {
Protocol string Protocol string
} }
func (_ *NetResponse) Description() string { var description = "TCP or UDP 'ping' given url and collect response time in seconds"
return "TCP or UDP 'ping' given url and collect response time in seconds"
// Description will return a short string to explain what the plugin does.
func (*NetResponse) Description() string {
return description
} }
var sampleConfig = ` var sampleConfig = `
@ -49,13 +52,17 @@ var sampleConfig = `
# expect = "ssh" # expect = "ssh"
` `
func (_ *NetResponse) SampleConfig() string { // SampleConfig will return a complete configuration example with details about each field.
func (*NetResponse) SampleConfig() string {
return sampleConfig return sampleConfig
} }
func (n *NetResponse) TcpGather() (map[string]interface{}, error) { // TCPGather will execute if there are TCP tests defined in the configuration.
// Prepare fields // It will return a map[string]interface{} for fields and a map[string]string for tags
fields := make(map[string]interface{}) func (n *NetResponse) TCPGather() (tags map[string]string, fields map[string]interface{}) {
// Prepare returns
tags = make(map[string]string)
fields = make(map[string]interface{})
// Start Timer // Start Timer
start := time.Now() start := time.Now()
// Connecting // Connecting
@ -65,11 +72,15 @@ func (n *NetResponse) TcpGather() (map[string]interface{}, error) {
// Handle error // Handle error
if err != nil { if err != nil {
if e, ok := err.(net.Error); ok && e.Timeout() { if e, ok := err.(net.Error); ok && e.Timeout() {
tags["result_text"] = "timeout"
fields["result_code"] = 1
fields["result_type"] = "timeout" fields["result_type"] = "timeout"
} else { } else {
tags["result_text"] = "connection_failed"
fields["result_code"] = 1
fields["result_type"] = "connection_failed" fields["result_type"] = "connection_failed"
} }
return fields, nil return tags, fields
} }
defer conn.Close() defer conn.Close()
// Send string if needed // Send string if needed
@ -92,30 +103,42 @@ func (n *NetResponse) TcpGather() (map[string]interface{}, error) {
responseTime = time.Since(start).Seconds() responseTime = time.Since(start).Seconds()
// Handle error // Handle error
if err != nil { if err != nil {
tags["result_text"] = "read_failed"
fields["result_code"] = 1
fields["string_found"] = false fields["string_found"] = false
fields["result_type"] = "read_failed" tags["result_type"] = "read_failed"
fields["success"] = 1
} else { } else {
// Looking for string in answer // Looking for string in answer
RegEx := regexp.MustCompile(`.*` + n.Expect + `.*`) RegEx := regexp.MustCompile(`.*` + n.Expect + `.*`)
find := RegEx.FindString(string(data)) find := RegEx.FindString(string(data))
if find != "" { if find != "" {
tags["result_text"] = "success"
fields["result_code"] = 0
fields["result_type"] = "success" fields["result_type"] = "success"
fields["string_found"] = true fields["string_found"] = true
} else { } else {
tags["result_text"] = "string_mismatch"
fields["result_code"] = 1
fields["result_type"] = "string_mismatch" fields["result_type"] = "string_mismatch"
fields["string_found"] = false fields["string_found"] = false
} }
} }
} else { } else {
tags["result_text"] = "success"
fields["result_code"] = 0
fields["result_type"] = "success" fields["result_type"] = "success"
} }
fields["response_time"] = responseTime fields["response_time"] = responseTime
return fields, nil return tags, fields
} }
func (n *NetResponse) UdpGather() (map[string]interface{}, error) { // UDPGather will execute if there are UDP tests defined in the configuration.
// Prepare fields // It will return a map[string]interface{} for fields and a map[string]string for tags
fields := make(map[string]interface{}) func (n *NetResponse) UDPGather() (tags map[string]string, fields map[string]interface{}) {
// Prepare returns
tags = make(map[string]string)
fields = make(map[string]interface{})
// Start Timer // Start Timer
start := time.Now() start := time.Now()
// Resolving // Resolving
@ -125,8 +148,10 @@ func (n *NetResponse) UdpGather() (map[string]interface{}, error) {
conn, err := net.DialUDP("udp", LocalAddr, udpAddr) conn, err := net.DialUDP("udp", LocalAddr, udpAddr)
// Handle error // Handle error
if err != nil { if err != nil {
tags["result_text"] = "connection_failed"
fields["result_code"] = 1
fields["result_type"] = "connection_failed" fields["result_type"] = "connection_failed"
return fields, nil return tags, fields
} }
defer conn.Close() defer conn.Close()
// Send string // Send string
@ -142,24 +167,35 @@ func (n *NetResponse) UdpGather() (map[string]interface{}, error) {
responseTime := time.Since(start).Seconds() responseTime := time.Since(start).Seconds()
// Handle error // Handle error
if err != nil { if err != nil {
tags["result_text"] = "read_failed"
fields["result_code"] = 1
fields["result_type"] = "read_failed" fields["result_type"] = "read_failed"
return fields, nil return tags, fields
} else { }
// Looking for string in answer // Looking for string in answer
RegEx := regexp.MustCompile(`.*` + n.Expect + `.*`) RegEx := regexp.MustCompile(`.*` + n.Expect + `.*`)
find := RegEx.FindString(string(buf)) find := RegEx.FindString(string(buf))
if find != "" { if find != "" {
tags["result_text"] = "success"
fields["result_code"] = 0
fields["result_type"] = "success" fields["result_type"] = "success"
fields["string_found"] = true fields["string_found"] = true
} else { } else {
tags["result_text"] = "string_mismatch"
fields["result_code"] = 1
fields["result_type"] = "string_mismatch" fields["result_type"] = "string_mismatch"
fields["string_found"] = false fields["string_found"] = false
} }
}
fields["response_time"] = responseTime fields["response_time"] = responseTime
return fields, nil
return tags, fields
} }
// 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 { func (n *NetResponse) Gather(acc telegraf.Accumulator) error {
// Set default values // Set default values
if n.Timeout.Duration == 0 { if n.Timeout.Duration == 0 {
@ -189,18 +225,23 @@ func (n *NetResponse) Gather(acc telegraf.Accumulator) error {
// Prepare data // Prepare data
tags := map[string]string{"server": host, "port": port} tags := map[string]string{"server": host, "port": port}
var fields map[string]interface{} var fields map[string]interface{}
var returnTags map[string]string
// Gather data // Gather data
if n.Protocol == "tcp" { if n.Protocol == "tcp" {
fields, err = n.TcpGather() returnTags, fields = n.TCPGather()
tags["protocol"] = "tcp" tags["protocol"] = "tcp"
} else if n.Protocol == "udp" { } else if n.Protocol == "udp" {
fields, err = n.UdpGather() returnTags, fields = n.UDPGather()
tags["protocol"] = "udp" tags["protocol"] = "udp"
} else { } else {
return errors.New("Bad protocol") return errors.New("Bad protocol")
} }
if err != nil { for key, value := range returnTags {
return err tags[key] = value
}
// Merge the tags
for k, v := range returnTags {
tags[k] = v
} }
// Add metrics // Add metrics
acc.AddFields("net_response", fields, tags) acc.AddFields("net_response", fields, tags)

View File

@ -13,6 +13,21 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestSample(t *testing.T) {
c := &NetResponse{}
output := c.SampleConfig()
if output != sampleConfig {
t.Error("Sample config doesn't match")
}
}
func TestDescription(t *testing.T) {
c := &NetResponse{}
output := c.Description()
if output != description {
t.Error("Description output is not correct")
}
}
func TestBadProtocol(t *testing.T) { func TestBadProtocol(t *testing.T) {
var acc testutil.Accumulator var acc testutil.Accumulator
// Init plugin // Init plugin
@ -26,6 +41,50 @@ func TestBadProtocol(t *testing.T) {
assert.Equal(t, "Bad protocol", err1.Error()) assert.Equal(t, "Bad protocol", err1.Error())
} }
func TestNoPort(t *testing.T) {
var acc testutil.Accumulator
c := NetResponse{
Protocol: "tcp",
Address: ":",
}
err1 := c.Gather(&acc)
require.Error(t, err1)
assert.Equal(t, "Bad port", err1.Error())
}
func TestAddressOnly(t *testing.T) {
var acc testutil.Accumulator
c := NetResponse{
Protocol: "tcp",
Address: "127.0.0.1",
}
err1 := c.Gather(&acc)
require.Error(t, err1)
assert.Equal(t, "address 127.0.0.1: missing port in address", err1.Error())
}
func TestSendExpectStrings(t *testing.T) {
var acc testutil.Accumulator
tc := NetResponse{
Protocol: "udp",
Address: "127.0.0.1:7",
Send: "",
Expect: "toast",
}
uc := NetResponse{
Protocol: "udp",
Address: "127.0.0.1:7",
Send: "toast",
Expect: "",
}
err1 := tc.Gather(&acc)
require.Error(t, err1)
assert.Equal(t, "Send string cannot be empty", err1.Error())
err2 := uc.Gather(&acc)
require.Error(t, err2)
assert.Equal(t, "Expected string cannot be empty", err2.Error())
}
func TestTCPError(t *testing.T) { func TestTCPError(t *testing.T) {
var acc testutil.Accumulator var acc testutil.Accumulator
// Init plugin // Init plugin
@ -39,12 +98,14 @@ func TestTCPError(t *testing.T) {
acc.AssertContainsTaggedFields(t, acc.AssertContainsTaggedFields(t,
"net_response", "net_response",
map[string]interface{}{ map[string]interface{}{
"result_code": 1,
"result_type": "connection_failed", "result_type": "connection_failed",
}, },
map[string]string{ map[string]string{
"server": "", "server": "",
"port": "9999", "port": "9999",
"protocol": "tcp", "protocol": "tcp",
"result_text": "connection_failed",
}, },
) )
} }
@ -77,11 +138,14 @@ func TestTCPOK1(t *testing.T) {
acc.AssertContainsTaggedFields(t, acc.AssertContainsTaggedFields(t,
"net_response", "net_response",
map[string]interface{}{ map[string]interface{}{
"result_code": 0,
"result_type": "success", "result_type": "success",
"string_found": true, "string_found": true,
"response_time": 1.0, "response_time": 1.0,
}, },
map[string]string{"server": "127.0.0.1", map[string]string{
"result_text": "success",
"server": "127.0.0.1",
"port": "2004", "port": "2004",
"protocol": "tcp", "protocol": "tcp",
}, },
@ -118,11 +182,14 @@ func TestTCPOK2(t *testing.T) {
acc.AssertContainsTaggedFields(t, acc.AssertContainsTaggedFields(t,
"net_response", "net_response",
map[string]interface{}{ map[string]interface{}{
"result_code": 1,
"result_type": "string_mismatch", "result_type": "string_mismatch",
"string_found": false, "string_found": false,
"response_time": 1.0, "response_time": 1.0,
}, },
map[string]string{"server": "127.0.0.1", map[string]string{
"result_text": "string_mismatch",
"server": "127.0.0.1",
"port": "2004", "port": "2004",
"protocol": "tcp", "protocol": "tcp",
}, },
@ -131,7 +198,7 @@ func TestTCPOK2(t *testing.T) {
wg.Wait() wg.Wait()
} }
func TestUDPrror(t *testing.T) { func TestUDPError(t *testing.T) {
var acc testutil.Accumulator var acc testutil.Accumulator
// Init plugin // Init plugin
c := NetResponse{ c := NetResponse{
@ -151,10 +218,12 @@ func TestUDPrror(t *testing.T) {
acc.AssertContainsTaggedFields(t, acc.AssertContainsTaggedFields(t,
"net_response", "net_response",
map[string]interface{}{ map[string]interface{}{
"result_code": 1,
"result_type": "read_failed", "result_type": "read_failed",
"response_time": 1.0, "response_time": 1.0,
}, },
map[string]string{ map[string]string{
"result_text": "read_failed",
"server": "", "server": "",
"port": "9999", "port": "9999",
"protocol": "udp", "protocol": "udp",
@ -190,11 +259,14 @@ func TestUDPOK1(t *testing.T) {
acc.AssertContainsTaggedFields(t, acc.AssertContainsTaggedFields(t,
"net_response", "net_response",
map[string]interface{}{ map[string]interface{}{
"result_code": 0,
"result_type": "success", "result_type": "success",
"string_found": true, "string_found": true,
"response_time": 1.0, "response_time": 1.0,
}, },
map[string]string{"server": "127.0.0.1", map[string]string{
"result_text": "success",
"server": "127.0.0.1",
"port": "2004", "port": "2004",
"protocol": "udp", "protocol": "udp",
}, },