From 9c8196fdacc6f71afcc973946cd4a7247804bb49 Mon Sep 17 00:00:00 2001 From: Sven Rebhan <36194019+srebhan@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:30:50 +0100 Subject: [PATCH] fix(inputs.bind): Convert counters to uint64 (#16015) --- CHANGELOG.md | 11 + plugins/inputs/bind/README.md | 5 + plugins/inputs/bind/bind.go | 9 +- plugins/inputs/bind/bind_test.go | 1269 ++++++++++++++++++++++++++- plugins/inputs/bind/sample.conf | 5 + plugins/inputs/bind/xml_stats_v3.go | 58 +- 6 files changed, 1333 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0052e27e..ff7832672 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ # Changelog +## Unreleased + +### Important Changes + +- PR [#16015](https://github.com/influxdata/telegraf/pull/16015) changes the internal + counters of the Bind plugin to unsigned integers matching the server + implementation. We keep backward compatibility by setting + `report_counters_as_int` to `true` by default to avoid type conflicts on the + output side. However, you should change this setting to `false` as soon as + possible to avoid invalid values and parsing errors with the v3 XML statistics. + ## v1.32.2 [2024-10-28] ### Bugfixes diff --git a/plugins/inputs/bind/README.md b/plugins/inputs/bind/README.md index 127c0cf2a..c28bec611 100644 --- a/plugins/inputs/bind/README.md +++ b/plugins/inputs/bind/README.md @@ -35,6 +35,11 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. # gather_memory_contexts = false # gather_views = false + ## Report xml v3 counters as integers instead of unsigned for backward + ## compatibility. Set this to false as soon as possible! + ## Values are clipped if exceeding the integer range. + # report_counters_as_int = true + ## Timeout for http requests made by bind nameserver # timeout = "4s" ``` diff --git a/plugins/inputs/bind/bind.go b/plugins/inputs/bind/bind.go index c78d3a5cb..713166063 100644 --- a/plugins/inputs/bind/bind.go +++ b/plugins/inputs/bind/bind.go @@ -18,10 +18,11 @@ import ( var sampleConfig string type Bind struct { - Urls []string - GatherMemoryContexts bool - GatherViews bool + Urls []string `toml:"urls"` + GatherMemoryContexts bool `toml:"gather_memory_contexts"` + GatherViews bool `toml:"gather_views"` Timeout config.Duration `toml:"timeout"` + CountersAsInt bool `toml:"report_counters_as_int"` client http.Client } @@ -84,5 +85,5 @@ func (b *Bind) gatherURL(addr *url.URL, acc telegraf.Accumulator) error { } func init() { - inputs.Add("bind", func() telegraf.Input { return &Bind{} }) + inputs.Add("bind", func() telegraf.Input { return &Bind{CountersAsInt: true} }) } diff --git a/plugins/inputs/bind/bind_test.go b/plugins/inputs/bind/bind_test.go index f501da092..35517e805 100644 --- a/plugins/inputs/bind/bind_test.go +++ b/plugins/inputs/bind/bind_test.go @@ -9,6 +9,9 @@ import ( "github.com/stretchr/testify/require" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/config" + "github.com/influxdata/telegraf/metric" "github.com/influxdata/telegraf/testutil" ) @@ -23,6 +26,7 @@ func TestBindJsonStats(t *testing.T) { Urls: []string{ts.URL + "/json/v1"}, GatherMemoryContexts: true, GatherViews: true, + CountersAsInt: true, client: http.Client{ Timeout: 4 * time.Second, }, @@ -197,6 +201,7 @@ func TestBindXmlStatsV2(t *testing.T) { Urls: []string{ts.URL + "/xml/v2"}, GatherMemoryContexts: true, GatherViews: true, + CountersAsInt: true, client: http.Client{ Timeout: 4 * time.Second, }, @@ -356,8 +361,7 @@ func TestBindXmlStatsV2(t *testing.T) { "port": port, } - fields := map[string]interface{}{} - + fields := make(map[string]interface{}, len(tc.values)) for _, val := range tc.values { fields[val.fieldKey] = val.fieldValue } @@ -403,6 +407,7 @@ func TestBindXmlStatsV3(t *testing.T) { Urls: []string{ts.URL + "/xml/v3"}, GatherMemoryContexts: true, GatherViews: true, + CountersAsInt: true, client: http.Client{ Timeout: 4 * time.Second, }, @@ -410,7 +415,6 @@ func TestBindXmlStatsV3(t *testing.T) { var acc testutil.Accumulator err = acc.GatherError(b.Gather) - require.NoError(t, err) // Use subtests for counters, since they are similar structure @@ -584,12 +588,10 @@ func TestBindXmlStatsV3(t *testing.T) { "port": port, } - fields := map[string]interface{}{} - + fields := make(map[string]interface{}, len(tc.values)) for _, val := range tc.values { fields[val.fieldKey] = val.fieldValue } - acc.AssertContainsTaggedFields(t, "bind_counter", fields, tags) }) } @@ -620,9 +622,1262 @@ func TestBindXmlStatsV3(t *testing.T) { }) } +func TestBindXmlStatsV3Signed(t *testing.T) { + // Setup a mock server to deliver the stats + ts := httptest.NewServer(http.FileServer(http.Dir("testdata"))) + url := ts.Listener.Addr().String() + host, port, err := net.SplitHostPort(url) + require.NoError(t, err) + defer ts.Close() + + // Setup the plugin + plugin := &Bind{ + Urls: []string{ts.URL + "/xml/v3"}, + GatherMemoryContexts: true, + GatherViews: true, + CountersAsInt: true, + Timeout: config.Duration(4 * time.Second), + } + require.NoError(t, plugin.Init()) + + // Create the expectations + expected := []telegraf.Metric{ + metric.New( + "bind_memory", + map[string]string{ + "url": url, + "source": host, + "port": port, + }, + map[string]interface{}{ + "block_size": int64(45875200), + "context_size": int64(10037400), + "in_use": int64(6000232), + "lost": int64(0), + "total_use": int64(777821909), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x55fb2e042de0", + "name": "main", + }, + map[string]interface{}{ + "in_use": int64(1454904), + "total": int64(2706043), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x55fb2e0507e0", + "name": "dst", + }, + map[string]interface{}{ + "in_use": int64(91776), + "total": int64(387478), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x55fb2e0938e0", + "name": "zonemgr-pool", + }, + map[string]interface{}{ + "in_use": int64(143776), + "total": int64(742986), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d00017d0", + "name": "threadkey", + }, + map[string]interface{}{ + "in_use": int64(0), + "total": int64(0), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d00475f0", + "name": "client", + }, + map[string]interface{}{ + "in_use": int64(8760), + "total": int64(267800), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d00dfca0", + "name": "cache", + }, + map[string]interface{}{ + "in_use": int64(83650), + "total": int64(288938), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d00eaa30", + "name": "cache_heap", + }, + map[string]interface{}{ + "in_use": int64(132096), + "total": int64(393216), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d01094e0", + "name": "res0", + }, + map[string]interface{}{ + "in_use": int64(0), + "total": int64(262144), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d0114270", + "name": "res1", + }, + map[string]interface{}{ + "in_use": int64(0), + "total": int64(0), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d011f000", + "name": "res2", + }, + map[string]interface{}{ + "in_use": int64(0), + "total": int64(0), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "opcode", + }, + map[string]interface{}{ + "IQUERY": int64(0), + "NOTIFY": int64(0), + "QUERY": int64(74941), + "STATUS": int64(0), + "UPDATE": int64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "qtype", + }, + map[string]interface{}{ + "A": int64(63672), + "AAAA": int64(5735), + "ANY": int64(22), + "MX": int64(618), + "NS": int64(373), + "PTR": int64(3393), + "RRSIG": int64(1), + "SOA": int64(18), + "SRV": int64(139), + "TXT": int64(970), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "nsstat", + }, + map[string]interface{}{ + "AuthQryRej": int64(0), + "DNS64": int64(0), + "ExpireOpt": int64(0), + "NSIDOpt": int64(0), + "OtherOpt": int64(59), + "QryAuthAns": int64(2752), + "QryDropped": int64(11), + "QryDuplicate": int64(11667), + "QryFORMERR": int64(0), + "QryFailure": int64(35), + "QryNXDOMAIN": int64(11610), + "QryNoauthAns": int64(60354), + "QryNxrrset": int64(2452), + "QryRecursion": int64(53750), + "QryReferral": int64(0), + "QrySERVFAIL": int64(122), + "QrySuccess": int64(49044), + "QryTCP": int64(258), + "QryUDP": int64(74648), + "RPZRewrites": int64(0), + "RateDropped": int64(0), + "RateSlipped": int64(0), + "RecQryRej": int64(35), + "RecursClients": int64(0), + "ReqBadEDNSVer": int64(0), + "ReqBadSIG": int64(0), + "ReqEdns0": int64(9250), + "ReqSIG0": int64(0), + "ReqTCP": int64(260), + "ReqTSIG": int64(0), + "Requestv4": int64(74942), + "Requestv6": int64(0), + "RespEDNS0": int64(9250), + "RespSIG0": int64(0), + "RespTSIG": int64(0), + "Response": int64(63264), + "SitBadSize": int64(0), + "SitBadTime": int64(0), + "SitMatch": int64(0), + "SitNew": int64(0), + "SitNoMatch": int64(0), + "SitOpt": int64(0), + "TruncatedResp": int64(365), + "UpdateBadPrereq": int64(0), + "UpdateDone": int64(0), + "UpdateFail": int64(0), + "UpdateFwdFail": int64(0), + "UpdateRej": int64(0), + "UpdateReqFwd": int64(0), + "UpdateRespFwd": int64(0), + "XfrRej": int64(0), + "XfrReqDone": int64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "zonestat", + }, + map[string]interface{}{ + "AXFRReqv4": int64(0), + "AXFRReqv6": int64(0), + "IXFRReqv4": int64(0), + "IXFRReqv6": int64(0), + "NotifyInv4": int64(0), + "NotifyInv6": int64(0), + "NotifyOutv4": int64(2), + "NotifyOutv6": int64(0), + "NotifyRej": int64(0), + "SOAOutv4": int64(0), + "SOAOutv6": int64(0), + "XfrFail": int64(0), + "XfrSuccess": int64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "sockstat", + }, + map[string]interface{}{ + "FDWatchClose": int64(0), + "FDwatchConn": int64(0), + "FDwatchConnFail": int64(0), + "FDwatchRecvErr": int64(0), + "FDwatchSendErr": int64(0), + "FdwatchBindFail": int64(0), + "RawActive": int64(1), + "RawClose": int64(0), + "RawOpen": int64(1), + "RawOpenFail": int64(0), + "RawRecvErr": int64(0), + "TCP4Accept": int64(293), + "TCP4AcceptFail": int64(0), + "TCP4Active": int64(297), + "TCP4BindFail": int64(0), + "TCP4Close": int64(336), + "TCP4ConnFail": int64(0), + "TCP4Conn": int64(44), + "TCP4Open": int64(48), + "TCP4OpenFail": int64(0), + "TCP4RecvErr": int64(0), + "TCP4SendErr": int64(0), + "TCP6Accept": int64(0), + "TCP6AcceptFail": int64(0), + "TCP6Active": int64(0), + "TCP6BindFail": int64(0), + "TCP6Close": int64(0), + "TCP6Conn": int64(0), + "TCP6ConnFail": int64(0), + "TCP6Open": int64(0), + "TCP6OpenFail": int64(0), + "TCP6RecvErr": int64(0), + "TCP6SendErr": int64(0), + "UDP4Active": int64(4), + "UDP4BindFail": int64(1), + "UDP4Close": int64(92538), + "UDP4Conn": int64(92535), + "UDP4ConnFail": int64(0), + "UDP4Open": int64(92542), + "UDP4OpenFail": int64(0), + "UDP4RecvErr": int64(14), + "UDP4SendErr": int64(0), + "UDP6Active": int64(0), + "UDP6BindFail": int64(0), + "UDP6Close": int64(0), + "UDP6Conn": int64(0), + "UDP6ConnFail": int64(0), + "UDP6Open": int64(0), + "UDP6OpenFail": int64(0), + "UDP6RecvErr": int64(0), + "UDP6SendErr": int64(0), + "UnixAccept": int64(0), + "UnixAcceptFail": int64(0), + "UnixActive": int64(0), + "UnixBindFail": int64(0), + "UnixClose": int64(0), + "UnixConn": int64(0), + "UnixConnFail": int64(0), + "UnixOpen": int64(0), + "UnixOpenFail": int64(0), + "UnixRecvErr": int64(0), + "UnixSendErr": int64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "resqtype", + "view": "_default", + }, + map[string]interface{}{ + "A": int64(61568), + "AAAA": int64(3933), + "DNSKEY": int64(1699), + "DS": int64(13749), + "MX": int64(286), + "NS": int64(9126), + "PTR": int64(1249), + "SRV": int64(21), + "TXT": int64(942), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "resstats", + "view": "_default", + }, + map[string]interface{}{ + "BadEDNSVersion": int64(0), + "BucketSize": int64(31), + "EDNS0Fail": int64(0), + "FORMERR": int64(0), + "GlueFetchv4": int64(1398), + "GlueFetchv4Fail": int64(3), + "GlueFetchv6": int64(0), + "GlueFetchv6Fail": int64(0), + "Lame": int64(12), + "Mismatch": int64(0), + "NXDOMAIN": int64(8182), + "NumFetch": int64(0), + "OtherError": int64(0), + "QryRTT10": int64(0), + "QryRTT100": int64(45760), + "QryRTT1600": int64(75), + "QryRTT1600+": int64(0), + "QryRTT500": int64(45543), + "QryRTT800": int64(743), + "QueryAbort": int64(0), + "QueryCurTCP": int64(0), + "QueryCurUDP": int64(0), + "QuerySockFail": int64(0), + "QueryTimeout": int64(490), + "Queryv4": int64(92573), + "Queryv6": int64(0), + "REFUSED": int64(34), + "Responsev4": int64(92135), + "Responsev6": int64(0), + "Retry": int64(800), + "SERVFAIL": int64(318), + "ServerQuota": int64(0), + "SitClientOk": int64(0), + "SitClientOut": int64(0), + "SitIn": int64(0), + "SitOut": int64(0), + "Truncated": int64(42), + "ValAttempt": int64(90256), + "ValFail": int64(6), + "ValNegOk": int64(22850), + "ValOk": int64(67322), + "ZoneQuota": int64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "adbstat", + "view": "_default", + }, + map[string]interface{}{ + "entriescnt": int64(314), + "namescnt": int64(316), + "nentries": int64(1021), + "nnames": int64(1021), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "cachestats", + "view": "_default", + }, + map[string]interface{}{ + "CacheBuckets": int64(519), + "CacheHits": int64(1904593), + "CacheMisses": int64(96), + "CacheNodes": int64(769), + "DeleteLRU": int64(0), + "DeleteTTL": int64(47518), + "HeapMemInUse": int64(132096), + "HeapMemMax": int64(132096), + "HeapMemTotal": int64(393216), + "QueryHits": int64(336094), + "QueryMisses": int64(369336), + "TreeMemInUse": int64(392128), + "TreeMemMax": int64(828966), + "TreeMemTotal": int64(1464363), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "resstats", + "view": "_bind", + }, + map[string]interface{}{ + "BadEDNSVersion": int64(0), + "BucketSize": int64(31), + "EDNS0Fail": int64(0), + "FORMERR": int64(0), + "GlueFetchv4": int64(0), + "GlueFetchv4Fail": int64(0), + "GlueFetchv6": int64(0), + "GlueFetchv6Fail": int64(0), + "Lame": int64(0), + "Mismatch": int64(0), + "NXDOMAIN": int64(0), + "NumFetch": int64(0), + "OtherError": int64(0), + "QryRTT10": int64(0), + "QryRTT100": int64(0), + "QryRTT1600": int64(0), + "QryRTT1600+": int64(0), + "QryRTT500": int64(0), + "QryRTT800": int64(0), + "QueryAbort": int64(0), + "QueryCurTCP": int64(0), + "QueryCurUDP": int64(0), + "QuerySockFail": int64(0), + "QueryTimeout": int64(0), + "Queryv4": int64(0), + "Queryv6": int64(0), + "REFUSED": int64(0), + "Responsev4": int64(0), + "Responsev6": int64(0), + "Retry": int64(0), + "SERVFAIL": int64(0), + "ServerQuota": int64(0), + "SitClientOk": int64(0), + "SitClientOut": int64(0), + "SitIn": int64(0), + "SitOut": int64(0), + "Truncated": int64(0), + "ValAttempt": int64(0), + "ValFail": int64(0), + "ValNegOk": int64(0), + "ValOk": int64(0), + "ZoneQuota": int64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "adbstat", + "view": "_bind", + }, + map[string]interface{}{ + "entriescnt": int64(0), + "namescnt": int64(0), + "nentries": int64(1021), + "nnames": int64(1021), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "cachestats", + "view": "_bind", + }, + map[string]interface{}{ + "CacheBuckets": int64(64), + "CacheHits": int64(0), + "CacheMisses": int64(0), + "CacheNodes": int64(0), + "DeleteLRU": int64(0), + "DeleteTTL": int64(0), + "HeapMemInUse": int64(1024), + "HeapMemMax": int64(1024), + "HeapMemTotal": int64(262144), + "QueryHits": int64(0), + "QueryMisses": int64(0), + "TreeMemInUse": int64(29608), + "TreeMemMax": int64(29608), + "TreeMemTotal": int64(287392), + }, + time.Unix(0, 0), + ), + } + + // Gather and compare + var acc testutil.Accumulator + require.NoError(t, acc.GatherError(plugin.Gather)) + testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime()) +} +func TestBindXmlStatsV3Unsigned(t *testing.T) { + // Setup a mock server to deliver the stats + ts := httptest.NewServer(http.FileServer(http.Dir("testdata"))) + url := ts.Listener.Addr().String() + host, port, err := net.SplitHostPort(url) + require.NoError(t, err) + defer ts.Close() + + // Setup the plugin + plugin := &Bind{ + Urls: []string{ts.URL + "/xml/v3"}, + GatherMemoryContexts: true, + GatherViews: true, + Timeout: config.Duration(4 * time.Second), + } + require.NoError(t, plugin.Init()) + + // Create the expectations + expected := []telegraf.Metric{ + metric.New( + "bind_memory", + map[string]string{ + "url": url, + "source": host, + "port": port, + }, + map[string]interface{}{ + "block_size": uint64(45875200), + "context_size": uint64(10037400), + "in_use": uint64(6000232), + "lost": uint64(0), + "total_use": uint64(777821909), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x55fb2e042de0", + "name": "main", + }, + map[string]interface{}{ + "in_use": uint64(1454904), + "total": uint64(2706043), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x55fb2e0507e0", + "name": "dst", + }, + map[string]interface{}{ + "in_use": uint64(91776), + "total": uint64(387478), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x55fb2e0938e0", + "name": "zonemgr-pool", + }, + map[string]interface{}{ + "in_use": uint64(143776), + "total": uint64(742986), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d00017d0", + "name": "threadkey", + }, + map[string]interface{}{ + "in_use": uint64(0), + "total": uint64(0), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d00475f0", + "name": "client", + }, + map[string]interface{}{ + "in_use": uint64(8760), + "total": uint64(267800), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d00dfca0", + "name": "cache", + }, + map[string]interface{}{ + "in_use": uint64(83650), + "total": uint64(288938), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d00eaa30", + "name": "cache_heap", + }, + map[string]interface{}{ + "in_use": uint64(132096), + "total": uint64(393216), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d01094e0", + "name": "res0", + }, + map[string]interface{}{ + "in_use": uint64(0), + "total": uint64(262144), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d0114270", + "name": "res1", + }, + map[string]interface{}{ + "in_use": uint64(0), + "total": uint64(0), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_memory_context", + map[string]string{ + "url": url, + "source": host, + "port": port, + "id": "0x7f19d011f000", + "name": "res2", + }, + map[string]interface{}{ + "in_use": uint64(0), + "total": uint64(0), + }, + time.Unix(0, 0), + telegraf.Gauge, + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "opcode", + }, + map[string]interface{}{ + "IQUERY": uint64(0), + "NOTIFY": uint64(0), + "QUERY": uint64(74941), + "STATUS": uint64(0), + "UPDATE": uint64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "qtype", + }, + map[string]interface{}{ + "A": uint64(63672), + "AAAA": uint64(5735), + "ANY": uint64(22), + "MX": uint64(618), + "NS": uint64(373), + "PTR": uint64(3393), + "RRSIG": uint64(1), + "SOA": uint64(18), + "SRV": uint64(139), + "TXT": uint64(970), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "nsstat", + }, + map[string]interface{}{ + "AuthQryRej": uint64(0), + "DNS64": uint64(0), + "ExpireOpt": uint64(0), + "NSIDOpt": uint64(0), + "OtherOpt": uint64(59), + "QryAuthAns": uint64(2752), + "QryDropped": uint64(11), + "QryDuplicate": uint64(11667), + "QryFORMERR": uint64(0), + "QryFailure": uint64(35), + "QryNXDOMAIN": uint64(11610), + "QryNoauthAns": uint64(60354), + "QryNxrrset": uint64(2452), + "QryRecursion": uint64(53750), + "QryReferral": uint64(0), + "QrySERVFAIL": uint64(122), + "QrySuccess": uint64(49044), + "QryTCP": uint64(258), + "QryUDP": uint64(74648), + "RPZRewrites": uint64(0), + "RateDropped": uint64(0), + "RateSlipped": uint64(0), + "RecQryRej": uint64(35), + "RecursClients": uint64(0), + "ReqBadEDNSVer": uint64(0), + "ReqBadSIG": uint64(0), + "ReqEdns0": uint64(9250), + "ReqSIG0": uint64(0), + "ReqTCP": uint64(260), + "ReqTSIG": uint64(0), + "Requestv4": uint64(74942), + "Requestv6": uint64(0), + "RespEDNS0": uint64(9250), + "RespSIG0": uint64(0), + "RespTSIG": uint64(0), + "Response": uint64(63264), + "SitBadSize": uint64(0), + "SitBadTime": uint64(0), + "SitMatch": uint64(0), + "SitNew": uint64(0), + "SitNoMatch": uint64(0), + "SitOpt": uint64(0), + "TruncatedResp": uint64(365), + "UpdateBadPrereq": uint64(0), + "UpdateDone": uint64(0), + "UpdateFail": uint64(0), + "UpdateFwdFail": uint64(0), + "UpdateRej": uint64(0), + "UpdateReqFwd": uint64(0), + "UpdateRespFwd": uint64(0), + "XfrRej": uint64(0), + "XfrReqDone": uint64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "zonestat", + }, + map[string]interface{}{ + "AXFRReqv4": uint64(0), + "AXFRReqv6": uint64(0), + "IXFRReqv4": uint64(0), + "IXFRReqv6": uint64(0), + "NotifyInv4": uint64(0), + "NotifyInv6": uint64(0), + "NotifyOutv4": uint64(2), + "NotifyOutv6": uint64(0), + "NotifyRej": uint64(0), + "SOAOutv4": uint64(0), + "SOAOutv6": uint64(0), + "XfrFail": uint64(0), + "XfrSuccess": uint64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "sockstat", + }, + map[string]interface{}{ + "FDWatchClose": uint64(0), + "FDwatchConn": uint64(0), + "FDwatchConnFail": uint64(0), + "FDwatchRecvErr": uint64(0), + "FDwatchSendErr": uint64(0), + "FdwatchBindFail": uint64(0), + "RawActive": uint64(1), + "RawClose": uint64(0), + "RawOpen": uint64(1), + "RawOpenFail": uint64(0), + "RawRecvErr": uint64(0), + "TCP4Accept": uint64(293), + "TCP4AcceptFail": uint64(0), + "TCP4Active": uint64(297), + "TCP4BindFail": uint64(0), + "TCP4Close": uint64(336), + "TCP4ConnFail": uint64(0), + "TCP4Conn": uint64(44), + "TCP4Open": uint64(48), + "TCP4OpenFail": uint64(0), + "TCP4RecvErr": uint64(0), + "TCP4SendErr": uint64(0), + "TCP6Accept": uint64(0), + "TCP6AcceptFail": uint64(0), + "TCP6Active": uint64(0), + "TCP6BindFail": uint64(0), + "TCP6Close": uint64(0), + "TCP6Conn": uint64(0), + "TCP6ConnFail": uint64(0), + "TCP6Open": uint64(0), + "TCP6OpenFail": uint64(0), + "TCP6RecvErr": uint64(0), + "TCP6SendErr": uint64(0), + "UDP4Active": uint64(4), + "UDP4BindFail": uint64(1), + "UDP4Close": uint64(92538), + "UDP4Conn": uint64(92535), + "UDP4ConnFail": uint64(0), + "UDP4Open": uint64(92542), + "UDP4OpenFail": uint64(0), + "UDP4RecvErr": uint64(14), + "UDP4SendErr": uint64(0), + "UDP6Active": uint64(0), + "UDP6BindFail": uint64(0), + "UDP6Close": uint64(0), + "UDP6Conn": uint64(0), + "UDP6ConnFail": uint64(0), + "UDP6Open": uint64(0), + "UDP6OpenFail": uint64(0), + "UDP6RecvErr": uint64(0), + "UDP6SendErr": uint64(0), + "UnixAccept": uint64(0), + "UnixAcceptFail": uint64(0), + "UnixActive": uint64(0), + "UnixBindFail": uint64(0), + "UnixClose": uint64(0), + "UnixConn": uint64(0), + "UnixConnFail": uint64(0), + "UnixOpen": uint64(0), + "UnixOpenFail": uint64(0), + "UnixRecvErr": uint64(0), + "UnixSendErr": uint64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "resqtype", + "view": "_default", + }, + map[string]interface{}{ + "A": uint64(61568), + "AAAA": uint64(3933), + "DNSKEY": uint64(1699), + "DS": uint64(13749), + "MX": uint64(286), + "NS": uint64(9126), + "PTR": uint64(1249), + "SRV": uint64(21), + "TXT": uint64(942), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "resstats", + "view": "_default", + }, + map[string]interface{}{ + "BadEDNSVersion": uint64(0), + "BucketSize": uint64(31), + "EDNS0Fail": uint64(0), + "FORMERR": uint64(0), + "GlueFetchv4": uint64(1398), + "GlueFetchv4Fail": uint64(3), + "GlueFetchv6": uint64(0), + "GlueFetchv6Fail": uint64(0), + "Lame": uint64(12), + "Mismatch": uint64(0), + "NXDOMAIN": uint64(8182), + "NumFetch": uint64(0), + "OtherError": uint64(0), + "QryRTT10": uint64(0), + "QryRTT100": uint64(45760), + "QryRTT1600": uint64(75), + "QryRTT1600+": uint64(0), + "QryRTT500": uint64(45543), + "QryRTT800": uint64(743), + "QueryAbort": uint64(0), + "QueryCurTCP": uint64(0), + "QueryCurUDP": uint64(0), + "QuerySockFail": uint64(0), + "QueryTimeout": uint64(490), + "Queryv4": uint64(92573), + "Queryv6": uint64(0), + "REFUSED": uint64(34), + "Responsev4": uint64(92135), + "Responsev6": uint64(0), + "Retry": uint64(800), + "SERVFAIL": uint64(318), + "ServerQuota": uint64(0), + "SitClientOk": uint64(0), + "SitClientOut": uint64(0), + "SitIn": uint64(0), + "SitOut": uint64(0), + "Truncated": uint64(42), + "ValAttempt": uint64(90256), + "ValFail": uint64(6), + "ValNegOk": uint64(22850), + "ValOk": uint64(67322), + "ZoneQuota": uint64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "adbstat", + "view": "_default", + }, + map[string]interface{}{ + "entriescnt": uint64(314), + "namescnt": uint64(316), + "nentries": uint64(1021), + "nnames": uint64(1021), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "cachestats", + "view": "_default", + }, + map[string]interface{}{ + "CacheBuckets": uint64(519), + "CacheHits": uint64(1904593), + "CacheMisses": uint64(96), + "CacheNodes": uint64(769), + "DeleteLRU": uint64(0), + "DeleteTTL": uint64(47518), + "HeapMemInUse": uint64(132096), + "HeapMemMax": uint64(132096), + "HeapMemTotal": uint64(393216), + "QueryHits": uint64(336094), + "QueryMisses": uint64(369336), + "TreeMemInUse": uint64(392128), + "TreeMemMax": uint64(828966), + "TreeMemTotal": uint64(1464363), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "resstats", + "view": "_bind", + }, + map[string]interface{}{ + "BadEDNSVersion": uint64(0), + "BucketSize": uint64(31), + "EDNS0Fail": uint64(0), + "FORMERR": uint64(0), + "GlueFetchv4": uint64(0), + "GlueFetchv4Fail": uint64(0), + "GlueFetchv6": uint64(0), + "GlueFetchv6Fail": uint64(0), + "Lame": uint64(0), + "Mismatch": uint64(0), + "NXDOMAIN": uint64(0), + "NumFetch": uint64(0), + "OtherError": uint64(0), + "QryRTT10": uint64(0), + "QryRTT100": uint64(0), + "QryRTT1600": uint64(0), + "QryRTT1600+": uint64(0), + "QryRTT500": uint64(0), + "QryRTT800": uint64(0), + "QueryAbort": uint64(0), + "QueryCurTCP": uint64(0), + "QueryCurUDP": uint64(0), + "QuerySockFail": uint64(0), + "QueryTimeout": uint64(0), + "Queryv4": uint64(0), + "Queryv6": uint64(0), + "REFUSED": uint64(0), + "Responsev4": uint64(0), + "Responsev6": uint64(0), + "Retry": uint64(0), + "SERVFAIL": uint64(0), + "ServerQuota": uint64(0), + "SitClientOk": uint64(0), + "SitClientOut": uint64(0), + "SitIn": uint64(0), + "SitOut": uint64(0), + "Truncated": uint64(0), + "ValAttempt": uint64(0), + "ValFail": uint64(0), + "ValNegOk": uint64(0), + "ValOk": uint64(0), + "ZoneQuota": uint64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "adbstat", + "view": "_bind", + }, + map[string]interface{}{ + "entriescnt": uint64(0), + "namescnt": uint64(0), + "nentries": uint64(1021), + "nnames": uint64(1021), + }, + time.Unix(0, 0), + ), + metric.New( + "bind_counter", + map[string]string{ + "url": url, + "source": host, + "port": port, + "type": "cachestats", + "view": "_bind", + }, + map[string]interface{}{ + "CacheBuckets": uint64(64), + "CacheHits": uint64(0), + "CacheMisses": uint64(0), + "CacheNodes": uint64(0), + "DeleteLRU": uint64(0), + "DeleteTTL": uint64(0), + "HeapMemInUse": uint64(1024), + "HeapMemMax": uint64(1024), + "HeapMemTotal": uint64(262144), + "QueryHits": uint64(0), + "QueryMisses": uint64(0), + "TreeMemInUse": uint64(29608), + "TreeMemMax": uint64(29608), + "TreeMemTotal": uint64(287392), + }, + time.Unix(0, 0), + ), + } + + // Gather and compare + var acc testutil.Accumulator + require.NoError(t, acc.GatherError(plugin.Gather)) + testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime()) +} + func TestBindUnparsableURL(t *testing.T) { b := Bind{ - Urls: []string{"://example.com"}, + Urls: []string{"://example.com"}, + CountersAsInt: true, } var acc testutil.Accumulator diff --git a/plugins/inputs/bind/sample.conf b/plugins/inputs/bind/sample.conf index 955095370..bcfb22628 100644 --- a/plugins/inputs/bind/sample.conf +++ b/plugins/inputs/bind/sample.conf @@ -6,5 +6,10 @@ # gather_memory_contexts = false # gather_views = false + ## Report xml v3 counters as integers instead of unsigned for backward + ## compatibility. Set this to false as soon as possible! + ## Values are clipped if exceeding the integer range. + # report_counters_as_int = true + ## Timeout for http requests made by bind nameserver # timeout = "4s" diff --git a/plugins/inputs/bind/xml_stats_v3.go b/plugins/inputs/bind/xml_stats_v3.go index 3d953ad29..74d5941ed 100644 --- a/plugins/inputs/bind/xml_stats_v3.go +++ b/plugins/inputs/bind/xml_stats_v3.go @@ -3,6 +3,7 @@ package bind import ( "encoding/xml" "fmt" + "math" "net" "net/http" "net/url" @@ -27,15 +28,15 @@ type v3Memory struct { // Omitted nodes: references, maxinuse, blocksize, pools, hiwater, lowater ID string `xml:"id"` Name string `xml:"name"` - Total int64 `xml:"total"` - InUse int64 `xml:"inuse"` + Total uint64 `xml:"total"` + InUse uint64 `xml:"inuse"` } `xml:"contexts>context"` Summary struct { - TotalUse int64 - InUse int64 - BlockSize int64 - ContextSize int64 - Lost int64 + TotalUse uint64 + InUse uint64 + BlockSize uint64 + ContextSize uint64 + Lost uint64 } `xml:"summary"` } @@ -53,7 +54,7 @@ type v3View struct { Name string `xml:"name,attr"` RRSets []struct { Name string `xml:"name"` - Value int64 `xml:"counter"` + Value uint64 `xml:"counter"` } `xml:"rrset"` } `xml:"cache"` } @@ -63,7 +64,7 @@ type v3CounterGroup struct { Type string `xml:"type,attr"` Counters []struct { Name string `xml:"name,attr"` - Value int64 `xml:",chardata"` + Value uint64 `xml:",chardata"` } `xml:"counter"` } @@ -83,8 +84,15 @@ func (b *Bind) addStatsXMLv3(stats v3Stats, acc telegraf.Accumulator, hostPort s } tags := map[string]string{"url": hostPort, "source": host, "port": port, "type": cg.Type} - - grouper.Add("bind_counter", tags, ts, c.Name, c.Value) + var v interface{} = c.Value + if b.CountersAsInt { + if c.Value < math.MaxInt64 { + v = int64(c.Value) + } else { + v = int64(math.MaxInt64) + } + } + grouper.Add("bind_counter", tags, ts, c.Name, v) } } @@ -96,6 +104,7 @@ func (b *Bind) addStatsXMLv3(stats v3Stats, acc telegraf.Accumulator, hostPort s "context_size": stats.Memory.Summary.ContextSize, "lost": stats.Memory.Summary.Lost, } + b.postProcessFields(fields) acc.AddGauge("bind_memory", fields, map[string]string{"url": hostPort, "source": host, "port": port}) // Detailed, per-context memory stats @@ -104,6 +113,7 @@ func (b *Bind) addStatsXMLv3(stats v3Stats, acc telegraf.Accumulator, hostPort s tags := map[string]string{"url": hostPort, "source": host, "port": port, "id": c.ID, "name": c.Name} fields := map[string]interface{}{"total": c.Total, "in_use": c.InUse} + b.postProcessFields(fields) acc.AddGauge("bind_memory_context", fields, tags) } } @@ -120,8 +130,15 @@ func (b *Bind) addStatsXMLv3(stats v3Stats, acc telegraf.Accumulator, hostPort s "view": v.Name, "type": cg.Type, } - - grouper.Add("bind_counter", tags, ts, c.Name, c.Value) + var v interface{} = c.Value + if b.CountersAsInt { + if c.Value < math.MaxInt64 { + v = int64(c.Value) + } else { + v = int64(math.MaxInt64) + } + } + grouper.Add("bind_counter", tags, ts, c.Name, v) } } } @@ -170,3 +187,18 @@ func (b *Bind) readStatsXMLv3(addr *url.URL, acc telegraf.Accumulator) error { b.addStatsXMLv3(stats, acc, addr.Host) return nil } + +func (b *Bind) postProcessFields(fields map[string]interface{}) { + if !b.CountersAsInt { + return + } + for k, val := range fields { + if v, ok := val.(uint64); ok { + if v < math.MaxInt64 { + fields[k] = int64(v) + } else { + fields[k] = int64(math.MaxInt64) + } + } + } +}