From ef63908541b9c2facf14538b4188f20aad19d8fb Mon Sep 17 00:00:00 2001
From: Bob Shannon <bobmshannon@users.noreply.github.com>
Date: Fri, 14 Jul 2017 13:43:36 -0400
Subject: [PATCH] Add result_type field to net_response input plugin (#2990)

---
 plugins/inputs/net_response/README.md         | 15 ++++---
 plugins/inputs/net_response/net_response.go   | 23 ++++++++---
 .../inputs/net_response/net_response_test.go  | 40 ++++++++++++++++---
 3 files changed, 62 insertions(+), 16 deletions(-)

diff --git a/plugins/inputs/net_response/README.md b/plugins/inputs/net_response/README.md
index 16c73e8e..01c681b5 100644
--- a/plugins/inputs/net_response/README.md
+++ b/plugins/inputs/net_response/README.md
@@ -1,4 +1,4 @@
-# Example Input Plugin
+# Network Response Input Plugin
 
 The input plugin test UDP/TCP connections response time.
 It can also check response text.
@@ -59,7 +59,8 @@ It can also check response text.
 
 - net_response
     - response_time (float, seconds)
-    - string_found (bool) # Only if "expected: option is set
+    - result_type (string) # success, timeout, connection_failed, read_failed, string_mismatch
+    - [**DEPRECATED**] string_found (boolean)
 
 ### Tags:
 
@@ -72,7 +73,11 @@ It can also check response text.
 
 ```
 $ ./telegraf --config telegraf.conf --input-filter net_response --test
-net_response,server=192.168.2.2,port=22,protocol=tcp response_time=0.18070360500000002,string_found=true 1454785464182527094
-net_response,server=192.168.2.2,port=2222,protocol=tcp response_time=1.090124776,string_found=false 1454784433658942325
-
+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_type="success",response_time=0.088703864 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_type="read_failed" 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 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_type="connection_failed" 1499310362000000000
 ```
diff --git a/plugins/inputs/net_response/net_response.go b/plugins/inputs/net_response/net_response.go
index ad0de46c..75d0a328 100644
--- a/plugins/inputs/net_response/net_response.go
+++ b/plugins/inputs/net_response/net_response.go
@@ -64,7 +64,12 @@ func (n *NetResponse) TcpGather() (map[string]interface{}, error) {
 	responseTime := time.Since(start).Seconds()
 	// Handle error
 	if err != nil {
-		return nil, err
+		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
@@ -88,17 +93,21 @@ func (n *NetResponse) TcpGather() (map[string]interface{}, error) {
 		// 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
@@ -114,11 +123,12 @@ func (n *NetResponse) UdpGather() (map[string]interface{}, error) {
 	LocalAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
 	// Connecting
 	conn, err := net.DialUDP("udp", LocalAddr, udpAddr)
-	defer conn.Close()
 	// Handle error
 	if err != nil {
-		return nil, err
+		fields["result_type"] = "connection_failed"
+		return fields, nil
 	}
+	defer conn.Close()
 	// Send string
 	msg := []byte(n.Send)
 	conn.Write(msg)
@@ -132,14 +142,17 @@ func (n *NetResponse) UdpGather() (map[string]interface{}, error) {
 	responseTime := time.Since(start).Seconds()
 	// Handle error
 	if err != nil {
-		return nil, err
+		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
 		}
 	}
diff --git a/plugins/inputs/net_response/net_response_test.go b/plugins/inputs/net_response/net_response_test.go
index a005c06f..c7bfb579 100644
--- a/plugins/inputs/net_response/net_response_test.go
+++ b/plugins/inputs/net_response/net_response_test.go
@@ -2,7 +2,6 @@ package net_response
 
 import (
 	"net"
-	"regexp"
 	"sync"
 	"testing"
 	"time"
@@ -36,8 +35,18 @@ func TestTCPError(t *testing.T) {
 	}
 	// Error
 	err1 := c.Gather(&acc)
-	require.Error(t, err1)
-	assert.Contains(t, err1.Error(), "getsockopt: connection refused")
+	require.NoError(t, err1)
+	acc.AssertContainsTaggedFields(t,
+		"net_response",
+		map[string]interface{}{
+			"result_type": "connection_failed",
+		},
+		map[string]string{
+			"server":   "",
+			"port":     "9999",
+			"protocol": "tcp",
+		},
+	)
 }
 
 func TestTCPOK1(t *testing.T) {
@@ -68,6 +77,7 @@ func TestTCPOK1(t *testing.T) {
 	acc.AssertContainsTaggedFields(t,
 		"net_response",
 		map[string]interface{}{
+			"result_type":   "success",
 			"string_found":  true,
 			"response_time": 1.0,
 		},
@@ -108,6 +118,7 @@ func TestTCPOK2(t *testing.T) {
 	acc.AssertContainsTaggedFields(t,
 		"net_response",
 		map[string]interface{}{
+			"result_type":   "string_mismatch",
 			"string_found":  false,
 			"response_time": 1.0,
 		},
@@ -129,10 +140,26 @@ func TestUDPrror(t *testing.T) {
 		Expect:   "test",
 		Protocol: "udp",
 	}
-	// Error
+	// Gather
 	err1 := c.Gather(&acc)
-	require.Error(t, err1)
-	assert.Regexp(t, regexp.MustCompile(`read udp 127.0.0.1:[0-9]*->127.0.0.1:9999: recvfrom: connection refused`), err1.Error())
+	// Override response time
+	for _, p := range acc.Metrics {
+		p.Fields["response_time"] = 1.0
+	}
+	// Error
+	require.NoError(t, err1)
+	acc.AssertContainsTaggedFields(t,
+		"net_response",
+		map[string]interface{}{
+			"result_type":   "read_failed",
+			"response_time": 1.0,
+		},
+		map[string]string{
+			"server":   "",
+			"port":     "9999",
+			"protocol": "udp",
+		},
+	)
 }
 
 func TestUDPOK1(t *testing.T) {
@@ -163,6 +190,7 @@ func TestUDPOK1(t *testing.T) {
 	acc.AssertContainsTaggedFields(t,
 		"net_response",
 		map[string]interface{}{
+			"result_type":   "success",
 			"string_found":  true,
 			"response_time": 1.0,
 		},
-- 
GitLab