Add TTL field to ping input (#5556)

This commit is contained in:
Dheeraj Dwivedi 2019-03-09 04:00:38 +05:30 committed by Daniel Nelson
parent 7da57fe5f5
commit 80155029c4
3 changed files with 100 additions and 37 deletions

View File

@ -70,6 +70,7 @@ LimitNOFILE=4096
- packets_transmitted (integer)
- packets_received (integer)
- percent_packets_loss (float)
- ttl (integer, Not available on Windows)
- average_response_ms (integer)
- minimum_response_ms (integer)
- maximum_response_ms (integer)
@ -92,5 +93,5 @@ ping,url=example.org result_code=0i,average_response_ms=7i,maximum_response_ms=9
**Linux:**
```
ping,url=example.org average_response_ms=23.066,maximum_response_ms=24.64,minimum_response_ms=22.451,packets_received=5i,packets_transmitted=5i,percent_packet_loss=0,result_code=0i,standard_deviation_ms=0.809 1535747258000000000
ping,url=example.org average_response_ms=23.066,ttl=63,maximum_response_ms=24.64,minimum_response_ms=22.451,packets_received=5i,packets_transmitted=5i,percent_packet_loss=0,result_code=0i,standard_deviation_ms=0.809 1535747258000000000
```

View File

@ -7,6 +7,7 @@ import (
"fmt"
"net"
"os/exec"
"regexp"
"runtime"
"strconv"
"strings"
@ -151,7 +152,7 @@ func (p *Ping) pingToURL(u string, acc telegraf.Accumulator) {
}
}
trans, rec, min, avg, max, stddev, err := processPingOutput(out)
trans, rec, ttl, min, avg, max, stddev, err := processPingOutput(out)
if err != nil {
// fatal error
acc.AddError(fmt.Errorf("%s: %s", err, u))
@ -164,6 +165,9 @@ func (p *Ping) pingToURL(u string, acc telegraf.Accumulator) {
fields["packets_transmitted"] = trans
fields["packets_received"] = rec
fields["percent_packet_loss"] = loss
if ttl >= 0 {
fields["ttl"] = ttl
}
if min >= 0 {
fields["minimum_response_ms"] = min
}
@ -253,50 +257,74 @@ func (p *Ping) args(url string, system string) []string {
// round-trip min/avg/max/stddev = 34.843/43.508/52.172/8.664 ms
//
// It returns (<transmitted packets>, <received packets>, <average response>)
func processPingOutput(out string) (int, int, float64, float64, float64, float64, error) {
var trans, recv int
func processPingOutput(out string) (int, int, int, float64, float64, float64, float64, error) {
var trans, recv, ttl int = 0, 0, -1
var min, avg, max, stddev float64 = -1.0, -1.0, -1.0, -1.0
// Set this error to nil if we find a 'transmitted' line
err := errors.New("Fatal error processing ping output")
lines := strings.Split(out, "\n")
for _, line := range lines {
if strings.Contains(line, "transmitted") &&
// Reading only first TTL, ignoring other TTL messages
if ttl == -1 && strings.Contains(line, "ttl=") {
ttl, err = getTTL(line)
} else if strings.Contains(line, "transmitted") &&
strings.Contains(line, "received") {
stats := strings.Split(line, ", ")
// Transmitted packets
trans, err = strconv.Atoi(strings.Split(stats[0], " ")[0])
trans, recv, err = getPacketStats(line, trans, recv)
if err != nil {
return trans, recv, min, avg, max, stddev, err
}
// Received packets
recv, err = strconv.Atoi(strings.Split(stats[1], " ")[0])
if err != nil {
return trans, recv, min, avg, max, stddev, err
return trans, recv, ttl, min, avg, max, stddev, err
}
} else if strings.Contains(line, "min/avg/max") {
stats := strings.Split(line, " ")[3]
data := strings.Split(stats, "/")
min, err = strconv.ParseFloat(data[0], 64)
min, avg, max, stddev, err = checkRoundTripTimeStats(line, min, avg, max, stddev)
if err != nil {
return trans, recv, min, avg, max, stddev, err
}
avg, err = strconv.ParseFloat(data[1], 64)
if err != nil {
return trans, recv, min, avg, max, stddev, err
}
max, err = strconv.ParseFloat(data[2], 64)
if err != nil {
return trans, recv, min, avg, max, stddev, err
}
if len(data) == 4 {
stddev, err = strconv.ParseFloat(data[3], 64)
if err != nil {
return trans, recv, min, avg, max, stddev, err
}
return trans, recv, ttl, min, avg, max, stddev, err
}
}
}
return trans, recv, min, avg, max, stddev, err
return trans, recv, ttl, min, avg, max, stddev, err
}
func getPacketStats(line string, trans, recv int) (int, int, error) {
stats := strings.Split(line, ", ")
// Transmitted packets
trans, err := strconv.Atoi(strings.Split(stats[0], " ")[0])
if err != nil {
return trans, recv, err
}
// Received packets
recv, err = strconv.Atoi(strings.Split(stats[1], " ")[0])
return trans, recv, err
}
func getTTL(line string) (int, error) {
ttlLine := regexp.MustCompile(`ttl=(\d+)`)
ttlMatch := ttlLine.FindStringSubmatch(line)
return strconv.Atoi(ttlMatch[1])
}
func checkRoundTripTimeStats(line string, min, avg, max,
stddev float64) (float64, float64, float64, float64, error) {
stats := strings.Split(line, " ")[3]
data := strings.Split(stats, "/")
min, err := strconv.ParseFloat(data[0], 64)
if err != nil {
return min, avg, max, stddev, err
}
avg, err = strconv.ParseFloat(data[1], 64)
if err != nil {
return min, avg, max, stddev, err
}
max, err = strconv.ParseFloat(data[2], 64)
if err != nil {
return min, avg, max, stddev, err
}
if len(data) == 4 {
stddev, err = strconv.ParseFloat(data[3], 64)
if err != nil {
return min, avg, max, stddev, err
}
}
return min, avg, max, stddev, err
}
func init() {

View File

@ -61,8 +61,9 @@ ping: -i interval too short: Operation not permitted
// Test that ping command output is processed properly
func TestProcessPingOutput(t *testing.T) {
trans, rec, min, avg, max, stddev, err := processPingOutput(bsdPingOutput)
trans, rec, ttl, min, avg, max, stddev, err := processPingOutput(bsdPingOutput)
assert.NoError(t, err)
assert.Equal(t, 55, ttl, "ttl value is 55")
assert.Equal(t, 5, trans, "5 packets were transmitted")
assert.Equal(t, 5, rec, "5 packets were transmitted")
assert.InDelta(t, 15.087, min, 0.001)
@ -70,8 +71,9 @@ func TestProcessPingOutput(t *testing.T) {
assert.InDelta(t, 27.263, max, 0.001)
assert.InDelta(t, 4.076, stddev, 0.001)
trans, rec, min, avg, max, stddev, err = processPingOutput(linuxPingOutput)
trans, rec, ttl, min, avg, max, stddev, err = processPingOutput(linuxPingOutput)
assert.NoError(t, err)
assert.Equal(t, 63, ttl, "ttl value is 63")
assert.Equal(t, 5, trans, "5 packets were transmitted")
assert.Equal(t, 5, rec, "5 packets were transmitted")
assert.InDelta(t, 35.225, min, 0.001)
@ -79,8 +81,9 @@ func TestProcessPingOutput(t *testing.T) {
assert.InDelta(t, 51.806, max, 0.001)
assert.InDelta(t, 5.325, stddev, 0.001)
trans, rec, min, avg, max, stddev, err = processPingOutput(busyBoxPingOutput)
trans, rec, ttl, min, avg, max, stddev, err = processPingOutput(busyBoxPingOutput)
assert.NoError(t, err)
assert.Equal(t, 56, ttl, "ttl value is 56")
assert.Equal(t, 4, trans, "4 packets were transmitted")
assert.Equal(t, 4, rec, "4 packets were transmitted")
assert.InDelta(t, 15.810, min, 0.001)
@ -89,10 +92,37 @@ func TestProcessPingOutput(t *testing.T) {
assert.InDelta(t, -1.0, stddev, 0.001)
}
// Linux ping output with varying TTL
var linuxPingOutputWithVaryingTTL = `
PING www.google.com (216.58.218.164) 56(84) bytes of data.
64 bytes from host.net (216.58.218.164): icmp_seq=1 ttl=63 time=35.2 ms
64 bytes from host.net (216.58.218.164): icmp_seq=2 ttl=255 time=42.3 ms
64 bytes from host.net (216.58.218.164): icmp_seq=3 ttl=64 time=45.1 ms
64 bytes from host.net (216.58.218.164): icmp_seq=4 ttl=64 time=43.5 ms
64 bytes from host.net (216.58.218.164): icmp_seq=5 ttl=255 time=51.8 ms
--- www.google.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4010ms
rtt min/avg/max/mdev = 35.225/43.628/51.806/5.325 ms
`
// Test that ping command output is processed properly
func TestProcessPingOutputWithVaryingTTL(t *testing.T) {
trans, rec, ttl, min, avg, max, stddev, err := processPingOutput(linuxPingOutputWithVaryingTTL)
assert.NoError(t, err)
assert.Equal(t, 63, ttl, "ttl value is 63")
assert.Equal(t, 5, trans, "5 packets were transmitted")
assert.Equal(t, 5, rec, "5 packets were transmitted")
assert.InDelta(t, 35.225, min, 0.001)
assert.InDelta(t, 43.628, avg, 0.001)
assert.InDelta(t, 51.806, max, 0.001)
assert.InDelta(t, 5.325, stddev, 0.001)
}
// Test that processPingOutput returns an error when 'ping' fails to run, such
// as when an invalid argument is provided
func TestErrorProcessPingOutput(t *testing.T) {
_, _, _, _, _, _, err := processPingOutput(fatalPingOutput)
_, _, _, _, _, _, _, err := processPingOutput(fatalPingOutput)
assert.Error(t, err, "Error was expected from processPingOutput")
}
@ -160,6 +190,7 @@ func TestPingGather(t *testing.T) {
"packets_transmitted": 5,
"packets_received": 5,
"percent_packet_loss": 0.0,
"ttl": 63,
"minimum_response_ms": 35.225,
"average_response_ms": 43.628,
"maximum_response_ms": 51.806,
@ -201,6 +232,7 @@ func TestLossyPingGather(t *testing.T) {
"packets_transmitted": 5,
"packets_received": 3,
"percent_packet_loss": 40.0,
"ttl": 63,
"minimum_response_ms": 35.225,
"average_response_ms": 44.033,
"maximum_response_ms": 51.806,
@ -262,6 +294,8 @@ func TestFatalPingGather(t *testing.T) {
"Fatal ping should not have packet measurements")
assert.False(t, acc.HasMeasurement("percent_packet_loss"),
"Fatal ping should not have packet measurements")
assert.False(t, acc.HasMeasurement("ttl"),
"Fatal ping should not have packet measurements")
assert.False(t, acc.HasMeasurement("minimum_response_ms"),
"Fatal ping should not have packet measurements")
assert.False(t, acc.HasMeasurement("average_response_ms"),