Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(inputs.ntpq): Allow to specify reach output format #11594

Merged
merged 3 commits into from
Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions plugins/inputs/ntpq/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,25 @@ server (RMS of difference of multiple time samples, milliseconds);
```toml @sample.conf
# Get standard NTP query metrics, requires ntpq executable.
[[inputs.ntpq]]
## Servers to query with ntpq.
## If no server is given, the local machine is queried.
# servers = []

## If false, set the -n ntpq flag. Can reduce metric gather time.
## DEPRECATED since 1.24.0: add '-n' to 'options' instead to skip DNS lookup
# dns_lookup = true

## Options to pass to the ntpq command.
# options = "-p"

## Servers to query with ntpq.
## If no server is given, the local machine is queried.
# servers = []
## Output format for the 'reach' field.
## Available values are
## octal -- output as is in octal representation e.g. 377 (default)
## decimal -- convert value to decimal representation e.g. 371 -> 249
## count -- count the number of bits in the value. This represents
## the number of successful reaches, e.g. 37 -> 5
## ratio -- output the ratio of successful attempts e.g. 37 -> 5/8 = 0.625
# reach_format = "octal"
```

You can pass arbitrary options accepted by the `ntpq` command using the
Expand Down
75 changes: 62 additions & 13 deletions plugins/inputs/ntpq/ntpq.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"bytes"
_ "embed"
"fmt"
"math/bits"
"os/exec"
"regexp"
"strconv"
Expand All @@ -19,6 +20,7 @@ import (
)

// DO NOT REMOVE THE NEXT TWO LINES! This is required to embed the sampleConfig data.
//
//go:embed sample.conf
var sampleConfig string

Expand All @@ -32,9 +34,12 @@ type elementType int64
const (
None elementType = iota
Tag
FieldInt
FieldFloat
FieldDuration
FieldIntDecimal
FieldIntOctal
FieldIntRatio8
FieldIntBits
)

type column struct {
Expand All @@ -55,15 +60,16 @@ var fieldElements = map[string]elementType{
"delay": FieldFloat,
"jitter": FieldFloat,
"offset": FieldFloat,
"reach": FieldInt,
"reach": FieldIntDecimal,
"poll": FieldDuration,
"when": FieldDuration,
}

type NTPQ struct {
DNSLookup bool `toml:"dns_lookup" deprecated:"1.24.0;add '-n' to 'options' instead to skip DNS lookup"`
Options string `toml:"options"`
Servers []string `toml:"servers"`
DNSLookup bool `toml:"dns_lookup" deprecated:"1.24.0;add '-n' to 'options' instead to skip DNS lookup"`
Options string `toml:"options"`
Servers []string `toml:"servers"`
ReachFormat string `toml:"reach_format"`

runQ func(string) ([]byte, error)
}
Expand Down Expand Up @@ -104,6 +110,29 @@ func (n *NTPQ) Init() error {
return cmd.Output()
}
}

switch n.ReachFormat {
case "", "octal":
n.ReachFormat = "octal"
// Interpret the field as decimal integer returning
// the raw (octal) representation
fieldElements["reach"] = FieldIntDecimal
case "decimal":
// Interpret the field as octal integer returning
// decimal number representation
fieldElements["reach"] = FieldIntOctal
case "count":
// Interpret the field as bits set returning
// the number of bits set
fieldElements["reach"] = FieldIntBits
case "ratio":
// Interpret the field as ratio between the number of
// bits set and the maximum available bits set (8).
fieldElements["reach"] = FieldIntRatio8
default:
return fmt.Errorf("unknown 'reach_format' %q", n.ReachFormat)
}

return nil
}

Expand Down Expand Up @@ -187,14 +216,6 @@ func (n *NTPQ) gatherServer(acc telegraf.Accumulator, server string) {
continue
case Tag:
tags[col.name] = raw
case FieldInt:
value, err := strconv.ParseInt(raw, 10, 64)
if err != nil {
msg := fmt.Sprintf("%sparsing %q (%v) as int failed", msgPrefix, col.name, raw)
acc.AddError(fmt.Errorf("%s: %w", msg, err))
continue
}
fields[col.name] = value
case FieldFloat:
value, err := strconv.ParseFloat(raw, 64)
if err != nil {
Expand Down Expand Up @@ -223,6 +244,34 @@ func (n *NTPQ) gatherServer(acc telegraf.Accumulator, server string) {
continue
}
fields[col.name] = value * factor
case FieldIntDecimal:
value, err := strconv.ParseInt(raw, 10, 64)
if err != nil {
acc.AddError(fmt.Errorf("parsing %q (%v) as int failed: %w", col.name, raw, err))
continue
}
fields[col.name] = value
case FieldIntOctal:
value, err := strconv.ParseInt(raw, 8, 64)
if err != nil {
acc.AddError(fmt.Errorf("parsing %q (%v) as int failed: %w", col.name, raw, err))
continue
}
fields[col.name] = value
case FieldIntBits:
value, err := strconv.ParseUint(raw, 8, 64)
if err != nil {
acc.AddError(fmt.Errorf("parsing %q (%v) as int failed: %w", col.name, raw, err))
continue
}
fields[col.name] = bits.OnesCount64(value)
case FieldIntRatio8:
value, err := strconv.ParseUint(raw, 8, 64)
if err != nil {
acc.AddError(fmt.Errorf("parsing %q (%v) as int failed: %w", col.name, raw, err))
continue
}
fields[col.name] = float64(bits.OnesCount64(value)) / float64(8)
}
}

Expand Down
20 changes: 20 additions & 0 deletions plugins/inputs/ntpq/ntpq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@ import (
"github.com/influxdata/telegraf/testutil"
)

func TestInitInvalid(t *testing.T) {
tests := []struct {
name string
plugin *NTPQ
expected string
}{
{
name: "invalid reach_format",
plugin: &NTPQ{ReachFormat: "garbage"},
expected: `unknown 'reach_format' "garbage"`,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.EqualError(t, tt.plugin.Init(), tt.expected)
})
}
}

func TestCases(t *testing.T) {
// Get all directories in testdata
folders, err := os.ReadDir("testcases")
Expand Down
15 changes: 12 additions & 3 deletions plugins/inputs/ntpq/sample.conf
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
# Get standard NTP query metrics, requires ntpq executable.
[[inputs.ntpq]]
## Servers to query with ntpq.
## If no server is given, the local machine is queried.
# servers = []

## If false, set the -n ntpq flag. Can reduce metric gather time.
## DEPRECATED since 1.24.0: add '-n' to 'options' instead to skip DNS lookup
# dns_lookup = true

## Options to pass to the ntpq command.
# options = "-p"

## Servers to query with ntpq.
## If no server is given, the local machine is queried.
# servers = []
## Output format for the 'reach' field.
## Available values are
## octal -- output as is in octal representation e.g. 377 (default)
## decimal -- convert value to decimal representation e.g. 371 -> 249
## count -- count the number of bits in the value. This represents
## the number of successful reaches, e.g. 37 -> 5
## ratio -- output the ratio of successful attempts e.g. 37 -> 5/8 = 0.625
# reach_format = "octal"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ntpq,remote=uschi5-ntp-002.,state_prefix=*,refid=10.177.80.46,stratum=2,type=u when=101i,poll=256i,reach=5i,delay=51.016,offset=233.010,jitter=17.462 0
3 changes: 3 additions & 0 deletions plugins/inputs/ntpq/testcases/single_reach_count/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
remote refid st t when poll reach delay offset jitter
==============================================================================
*uschi5-ntp-002. 10.177.80.46 2 u 101 256 37 51.016 233.010 17.462
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[[inputs.ntpq]]
reach_format = "count"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ntpq,remote=uschi5-ntp-002.,state_prefix=*,refid=10.177.80.46,stratum=2,type=u when=101i,poll=256i,reach=31i,delay=51.016,offset=233.010,jitter=17.462 0
3 changes: 3 additions & 0 deletions plugins/inputs/ntpq/testcases/single_reach_decimal/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
remote refid st t when poll reach delay offset jitter
==============================================================================
*uschi5-ntp-002. 10.177.80.46 2 u 101 256 37 51.016 233.010 17.462
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[[inputs.ntpq]]
reach_format = "decimal"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ntpq,remote=uschi5-ntp-002.,state_prefix=*,refid=10.177.80.46,stratum=2,type=u when=101i,poll=256i,reach=37i,delay=51.016,offset=233.010,jitter=17.462 0
3 changes: 3 additions & 0 deletions plugins/inputs/ntpq/testcases/single_reach_octal/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
remote refid st t when poll reach delay offset jitter
==============================================================================
*uschi5-ntp-002. 10.177.80.46 2 u 101 256 37 51.016 233.010 17.462
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[[inputs.ntpq]]
reach_format = "octal"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ntpq,remote=uschi5-ntp-002.,state_prefix=*,refid=10.177.80.46,stratum=2,type=u when=101i,poll=256i,reach=0.625,delay=51.016,offset=233.010,jitter=17.462 0
3 changes: 3 additions & 0 deletions plugins/inputs/ntpq/testcases/single_reach_ratio/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
remote refid st t when poll reach delay offset jitter
==============================================================================
*uschi5-ntp-002. 10.177.80.46 2 u 101 256 37 51.016 233.010 17.462
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[[inputs.ntpq]]
reach_format = "ratio"