From 11aa13bb2f7235714a623524e6bdc363f2796384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Thu, 15 Oct 2026 20:42:01 +0200 Subject: [PATCH] dns: fix zone transfer counter mapping and count queued dynamic updates (#2718) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- UPGRADE.md | 7 +- docs/collector.dns.md | 6 +- internal/collector/dns/AGENTS.md | 11 +- internal/collector/dns/dns.go | 119 ++++++++++--------- internal/collector/dns/dns_internal_test.go | 122 ++++++++++++++++++++ internal/collector/dns/types.go | 8 +- 6 files changed, 208 insertions(+), 65 deletions(-) create mode 100644 internal/collector/dns/dns_internal_test.go diff --git a/UPGRADE.md b/UPGRADE.md index 24cd63d6..cbde334a 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -17,9 +17,9 @@ Most installations need no changes. Go through the checklist below and read the | You use the `container` collector | Replace `containerd-state-dir` with `cri-endpoint` | [container collector](#container-collector) | | You query `windows_gpu_info` | Replace the `phys` label with `device_number` | [GPU collector](#gpu-collector) | | You alert on BitLocker status | Replace `status="disabled"` with `status="off"` | [BitLocker status](#bitlocker-status) | -| You query Active Directory latency or security descriptor propagation, DFSR, Hyper-V, logical disk queue, cache, IIS, memory, RemoteFX, scheduled task, SMB server share or terminal services metrics | Update queries | [Renamed, deprecated and retyped metrics](#renamed-deprecated-and-retyped-metrics) | +| You query Active Directory latency or security descriptor propagation, DFSR, DNS dynamic update, Hyper-V, logical disk queue, cache, IIS, memory, RemoteFX, scheduled task, SMB server share or terminal services metrics | Update queries | [Renamed, deprecated and retyped metrics](#renamed-deprecated-and-retyped-metrics) | | You enable the `scheduled_task` collector and use tasks below `\Microsoft\` or missed runs | Set `--collector.scheduled_task.exclude` or `missed-runs` | [scheduled_task collector](#scheduled_task-collector) | -| You chart SQL Server latencies, SQL Server lock counts, DHCP denials, AD LSA writes, AD computer creations, AD FS database query times or RemoteFX skipped frames | Expect corrected values | [Corrected values](#corrected-values) | +| You chart SQL Server latencies, SQL Server lock counts, DHCP denials, AD LSA writes, AD computer creations, AD FS database query times, DNS zone transfers or RemoteFX skipped frames | Expect corrected values | [Corrected values](#corrected-values) | | You enable the `hyperv` or `mscluster` collector | Check the new default sub-collectors | [New default sub-collectors](#new-default-sub-collectors) | | You set scrape timeouts above 5 minutes or below the timeout margin | Review the new limits | [Scrape timeouts](#scrape-timeouts) | | You use the sample Grafana dashboard | Use Grafana 13 or later and import the new dashboard | [Dashboard and alerts](#dashboard-and-alerts) | @@ -252,6 +252,7 @@ Renamed: | `windows_iis_http_request_total_rejected_request` | `windows_iis_http_requests_rejected_total` | Now a counter, use `rate()`; label `app` | | `windows_iis_http_requests_arrival_rate` | `windows_iis_http_requests_arrived_total` | Was a running count, now a counter, use `rate()`; label `app` | | `windows_iis_http_requests_max_queue_item_age` | `windows_iis_http_requests_max_queue_item_age_seconds` | Left out while the queue is empty; label `app` | +| `windows_dns_dynamic_updates_queued` | `windows_dns_dynamic_updates_queued_total` | Now a counter, use `rate()` | The RemoteFX loss, FEC and retransmission gauges were not percentages. They exported the raw numerator of a Windows sample fraction, a running count that only grows. The numerator and its base are now two counters. Get the ratio with `rate(windows_remote_fx_net_lost_packets_total[5m]) / rate(windows_remote_fx_net_loss_sampled_packets_total[5m])`, and likewise with `windows_remote_fx_net_fec_packets_total` / `windows_remote_fx_net_fec_sampled_packets_total` and `windows_remote_fx_net_retransmitted_packets_total` / `windows_remote_fx_net_retransmission_sampled_packets_total`. @@ -323,6 +324,8 @@ These metrics keep their names, but 0.31 reported wrong values. Graphs show a st - `windows_adfs_db_artifact_query_time_seconds_total` and `windows_adfs_db_config_query_time_seconds_total` are now in seconds. 0.31 scaled the raw ticks by a fixed `1e-8`, so on a typical 10 MHz time base the values were 10 times too small. The new `windows_adfs_db_artifact_queries_total` and `windows_adfs_db_config_queries_total` count the queries, so you can compute the average query time. - `windows_dhcp_denied_due_to_nonmatch_total` now counts denials due to a non-match. In 0.31 it repeated the match denial count. - `windows_remote_fx_gfx_frames_skipped_insufficient_resource_total` with `resource="client"` and `resource="server"` now count the matching frames. In 0.31 the two label values were swapped. +- `windows_dns_zone_transfer_requests_sent_total{qtype="soa"}` now counts zone transfer SOA requests sent. In 0.31 it reported zone transfer requests received. +- `windows_dns_zone_transfer_success_received_total{qtype="incremental",protocol="udp"}` now counts successful UDP incremental zone transfers. In 0.31 it repeated the TCP count. - `windows_netframework_clrmemory_gc_time_percent` no longer overflows, and it is left out when Windows reports no base value. The `netframework` collector now reads the CLR counters through PDH instead of WMI; names, labels and types are unchanged. - The IIS URI cache flush and kernel cache item metrics read the right Windows counters, and the metadata cache hit metrics are no longer always `0`. - `windows_pagefile_limit_bytes` and `windows_pagefile_free_bytes` read the paging file size from the `Paging File` counter instead of the file system. In 0.31 a paging file that could not be read reported a size of `0` and a negative free space; it is now left out. diff --git a/docs/collector.dns.md b/docs/collector.dns.md index 3d1d4fe8..e3eeed72 100644 --- a/docs/collector.dns.md +++ b/docs/collector.dns.md @@ -22,14 +22,14 @@ Name | Description Name | Description | Type | Labels -----|-------------|------|------- `windows_dns_zone_transfer_requests_received_total` | Number of zone transfer requests (AXFR/IXFR) received by the master DNS server | counter | `qtype` -`windows_dns_zone_transfer_requests_sent_total` | Number of zone transfer requests (AXFR/IXFR) sent by the secondary DNS server | counter | `qtype` +`windows_dns_zone_transfer_requests_sent_total` | Number of zone transfer requests (AXFR/IXFR) and zone transfer SOA requests sent by the secondary DNS server | counter | `qtype` (`full`, `incremental`, `soa`) `windows_dns_zone_transfer_response_received_total` | Number of zone transfer responses (AXFR/IXFR) received by the secondary DNS server | counter | `qtype` `windows_dns_zone_transfer_success_received_total` | Number of successful zone transfers (AXFR/IXFR) received by the secondary DNS server | counter | `qtype`, `protocol` `windows_dns_zone_transfer_success_sent_total` | Number of successful zone transfers (AXFR/IXFR) of the master DNS server | counter | `qtype` `windows_dns_zone_transfer_failures_total` | Number of failed zone transfers of the master DNS server | counter | None `windows_dns_memory_used_bytes` | Current memory used by DNS server | gauge | `area` -`windows_dns_dynamic_updates_queued` | Number of dynamic updates queued by the DNS server | gauge | None -`windows_dns_dynamic_updates_received_total` | Number of secure update requests received by the DNS server | counter | `operation` +`windows_dns_dynamic_updates_queued_total` | Number of dynamic updates queued by the DNS server | counter | None +`windows_dns_dynamic_updates_received_total` | Number of dynamic update requests received by the DNS server that were no-operations (noop) or written to the database (written) | counter | `operation` `windows_dns_dynamic_updates_failures_total` | Number of dynamic updates which timed out or were rejected by the DNS server | counter | `reason` `windows_dns_notify_received_total` | Number of notifies received by the secondary DNS server | counter | None `windows_dns_notify_sent_total` | Number of notifies sent by the master DNS server | counter | None diff --git a/internal/collector/dns/AGENTS.md b/internal/collector/dns/AGENTS.md index 3705eae2..72c80d14 100644 --- a/internal/collector/dns/AGENTS.md +++ b/internal/collector/dns/AGENTS.md @@ -20,7 +20,11 @@ secondary server, so zone transfer counters stayed 0. - `Total Query Received` equals `UDP Query Received` + `TCP Query Received` (830 = 820 + 10); `Total Response Sent` likewise (825 = 815 + 10). - `Recursive Send TimeOuts` and `Recursive TimeOut/sec` are a twin pair with - different names; the collector reads the `/sec` one. + different names; the collector reads `Recursive Send TimeOuts`. +- `Dynamic Update Queued` is cumulative: it matches `Queue / Queued` in + `dnscmd /statistics 0x00000100` (and `Queued` in + `Get-DnsServerStatistics`), not the current `In Queue` value. Publish it as + a counter. - Eleven counters of the object have names that belong to other objects and no help text: `% User Time`, `Async Fast Reads/sec`, `Fast Read Not Possibles/sec`, `Fast Read Resource Misses/sec`, @@ -28,6 +32,11 @@ secondary server, so zone transfer counters stayed 0. `Data Flush Pages/sec`, `Level 2 TLB Fills/sec`, `Enumerations Server/sec` and `Virtual Bytes`. `Get-Counter -ListSet DNS` also lists a counter named `176`. All of them stayed 0; the collector ignores them (`_` fields). +- Compare live values with `dnscmd /statistics` or `Get-Counter "\DNS\*"`. + `ipconfig /registerdns` on a domain member raises the dynamic update + counters. +- `collectPerfData` publishes one sample; test value mapping with it through + a registry (see `dns_internal_test.go`). ## Counter reference diff --git a/internal/collector/dns/dns.go b/internal/collector/dns/dns.go index 0be06204..c0695cec 100644 --- a/internal/collector/dns/dns.go +++ b/internal/collector/dns/dns.go @@ -158,6 +158,19 @@ func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error { } func (c *Collector) buildMetricsCollector(logger *slog.Logger) error { + c.buildDescriptors() + + var err error + + c.perfDataCollector, err = pdh.NewCollector[perfDataCounterValues](logger.With(slog.String("collector", Name)), pdh.CounterTypeRaw, "DNS", pdh.InstancesAll) + if err != nil { + return fmt.Errorf("failed to create DNS collector: %w", err) + } + + return nil +} + +func (c *Collector) buildDescriptors() { c.zoneTransferRequestsReceived = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "zone_transfer_requests_received_total"), "Number of zone transfer requests (AXFR/IXFR) received by the master DNS server", @@ -166,7 +179,7 @@ func (c *Collector) buildMetricsCollector(logger *slog.Logger) error { ) c.zoneTransferRequestsSent = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "zone_transfer_requests_sent_total"), - "Number of zone transfer requests (AXFR/IXFR) sent by the secondary DNS server", + "Number of zone transfer requests (AXFR/IXFR) and zone transfer SOA requests sent by the secondary DNS server", []string{"qtype"}, nil, ) @@ -201,14 +214,14 @@ func (c *Collector) buildMetricsCollector(logger *slog.Logger) error { nil, ) c.dynamicUpdatesQueued = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "dynamic_updates_queued"), + prometheus.BuildFQName(types.Namespace, Name, "dynamic_updates_queued_total"), "Number of dynamic updates queued by the DNS server", nil, nil, ) c.dynamicUpdatesReceived = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "dynamic_updates_received_total"), - "Number of secure update requests received by the DNS server", + "Number of dynamic update requests received by the DNS server that were no-operations (noop) or written to the database (written)", []string{"operation"}, nil, ) @@ -297,15 +310,6 @@ func (c *Collector) buildMetricsCollector(logger *slog.Logger) error { []string{"name", "collection_name", "dns_server"}, nil, ) - - var err error - - c.perfDataCollector, err = pdh.NewCollector[perfDataCounterValues](logger.With(slog.String("collector", Name)), pdh.CounterTypeRaw, "DNS", pdh.InstancesAll) - if err != nil { - return fmt.Errorf("failed to create DNS collector: %w", err) - } - - return nil } func (c *Collector) buildErrorStatsCollector(miSession *mi.Session) error { @@ -357,59 +361,66 @@ func (c *Collector) collectMetrics(ch chan<- prometheus.Metric) error { return fmt.Errorf("failed to collect DNS metrics: %w", types.ErrNoDataUnexpected) } + c.collectPerfData(ch, c.perfDataObject[0]) + + return nil +} + +// collectPerfData publishes the metrics of one sample of the DNS object. +func (c *Collector) collectPerfData(ch chan<- prometheus.Metric, data perfDataCounterValues) { ch <- prometheus.MustNewConstMetric( c.zoneTransferRequestsReceived, prometheus.CounterValue, - c.perfDataObject[0].AxfrRequestReceived, + data.AxfrRequestReceived, "full", ) ch <- prometheus.MustNewConstMetric( c.zoneTransferRequestsReceived, prometheus.CounterValue, - c.perfDataObject[0].IxfrRequestReceived, + data.IxfrRequestReceived, "incremental", ) ch <- prometheus.MustNewConstMetric( c.zoneTransferRequestsSent, prometheus.CounterValue, - c.perfDataObject[0].AxfrRequestSent, + data.AxfrRequestSent, "full", ) ch <- prometheus.MustNewConstMetric( c.zoneTransferRequestsSent, prometheus.CounterValue, - c.perfDataObject[0].IxfrRequestSent, + data.IxfrRequestSent, "incremental", ) ch <- prometheus.MustNewConstMetric( c.zoneTransferRequestsSent, prometheus.CounterValue, - c.perfDataObject[0].ZoneTransferSOARequestSent, + data.ZoneTransferSOARequestSent, "soa", ) ch <- prometheus.MustNewConstMetric( c.zoneTransferResponsesReceived, prometheus.CounterValue, - c.perfDataObject[0].AxfrResponseReceived, + data.AxfrResponseReceived, "full", ) ch <- prometheus.MustNewConstMetric( c.zoneTransferResponsesReceived, prometheus.CounterValue, - c.perfDataObject[0].IxfrResponseReceived, + data.IxfrResponseReceived, "incremental", ) ch <- prometheus.MustNewConstMetric( c.zoneTransferSuccessReceived, prometheus.CounterValue, - c.perfDataObject[0].AxfrSuccessReceived, + data.AxfrSuccessReceived, "full", "tcp", ) @@ -417,7 +428,7 @@ func (c *Collector) collectMetrics(ch chan<- prometheus.Metric) error { ch <- prometheus.MustNewConstMetric( c.zoneTransferSuccessReceived, prometheus.CounterValue, - c.perfDataObject[0].IxfrTCPSuccessReceived, + data.IxfrTCPSuccessReceived, "incremental", "tcp", ) @@ -425,7 +436,7 @@ func (c *Collector) collectMetrics(ch chan<- prometheus.Metric) error { ch <- prometheus.MustNewConstMetric( c.zoneTransferSuccessReceived, prometheus.CounterValue, - c.perfDataObject[0].IxfrTCPSuccessReceived, + data.IxfrUDPSuccessReceived, "incremental", "udp", ) @@ -433,204 +444,202 @@ func (c *Collector) collectMetrics(ch chan<- prometheus.Metric) error { ch <- prometheus.MustNewConstMetric( c.zoneTransferSuccessSent, prometheus.CounterValue, - c.perfDataObject[0].AxfrSuccessSent, + data.AxfrSuccessSent, "full", ) ch <- prometheus.MustNewConstMetric( c.zoneTransferSuccessSent, prometheus.CounterValue, - c.perfDataObject[0].IxfrSuccessSent, + data.IxfrSuccessSent, "incremental", ) ch <- prometheus.MustNewConstMetric( c.zoneTransferFailures, prometheus.CounterValue, - c.perfDataObject[0].ZoneTransferFailure, + data.ZoneTransferFailure, ) ch <- prometheus.MustNewConstMetric( c.memoryUsedBytes, prometheus.GaugeValue, - c.perfDataObject[0].CachingMemory, + data.CachingMemory, "caching", ) ch <- prometheus.MustNewConstMetric( c.memoryUsedBytes, prometheus.GaugeValue, - c.perfDataObject[0].DatabaseNodeMemory, + data.DatabaseNodeMemory, "database_node", ) ch <- prometheus.MustNewConstMetric( c.memoryUsedBytes, prometheus.GaugeValue, - c.perfDataObject[0].NbStatMemory, + data.NbStatMemory, "nbstat", ) ch <- prometheus.MustNewConstMetric( c.memoryUsedBytes, prometheus.GaugeValue, - c.perfDataObject[0].RecordFlowMemory, + data.RecordFlowMemory, "record_flow", ) ch <- prometheus.MustNewConstMetric( c.memoryUsedBytes, prometheus.GaugeValue, - c.perfDataObject[0].TcpMessageMemory, + data.TcpMessageMemory, "tcp_message", ) ch <- prometheus.MustNewConstMetric( c.memoryUsedBytes, prometheus.GaugeValue, - c.perfDataObject[0].UdpMessageMemory, + data.UdpMessageMemory, "udp_message", ) ch <- prometheus.MustNewConstMetric( c.dynamicUpdatesReceived, prometheus.CounterValue, - c.perfDataObject[0].DynamicUpdateNoOperation, + data.DynamicUpdateNoOperation, "noop", ) ch <- prometheus.MustNewConstMetric( c.dynamicUpdatesReceived, prometheus.CounterValue, - c.perfDataObject[0].DynamicUpdateWrittenToDatabase, + data.DynamicUpdateWrittenToDatabase, "written", ) ch <- prometheus.MustNewConstMetric( c.dynamicUpdatesQueued, - prometheus.GaugeValue, - c.perfDataObject[0].DynamicUpdateQueued, + prometheus.CounterValue, + data.DynamicUpdateQueued, ) ch <- prometheus.MustNewConstMetric( c.dynamicUpdatesFailures, prometheus.CounterValue, - c.perfDataObject[0].DynamicUpdateRejected, + data.DynamicUpdateRejected, "rejected", ) ch <- prometheus.MustNewConstMetric( c.dynamicUpdatesFailures, prometheus.CounterValue, - c.perfDataObject[0].DynamicUpdateTimeOuts, + data.DynamicUpdateTimeOuts, "timeout", ) ch <- prometheus.MustNewConstMetric( c.notifyReceived, prometheus.CounterValue, - c.perfDataObject[0].NotifyReceived, + data.NotifyReceived, ) ch <- prometheus.MustNewConstMetric( c.notifySent, prometheus.CounterValue, - c.perfDataObject[0].NotifySent, + data.NotifySent, ) ch <- prometheus.MustNewConstMetric( c.recursiveQueries, prometheus.CounterValue, - c.perfDataObject[0].RecursiveQueries, + data.RecursiveQueries, ) ch <- prometheus.MustNewConstMetric( c.recursiveQueryFailures, prometheus.CounterValue, - c.perfDataObject[0].RecursiveQueryFailure, + data.RecursiveQueryFailure, ) ch <- prometheus.MustNewConstMetric( c.recursiveQuerySendTimeouts, prometheus.CounterValue, - c.perfDataObject[0].RecursiveSendTimeOuts, + data.RecursiveSendTimeOuts, ) ch <- prometheus.MustNewConstMetric( c.queries, prometheus.CounterValue, - c.perfDataObject[0].TcpQueryReceived, + data.TcpQueryReceived, "tcp", ) ch <- prometheus.MustNewConstMetric( c.queries, prometheus.CounterValue, - c.perfDataObject[0].UdpQueryReceived, + data.UdpQueryReceived, "udp", ) ch <- prometheus.MustNewConstMetric( c.responses, prometheus.CounterValue, - c.perfDataObject[0].TcpResponseSent, + data.TcpResponseSent, "tcp", ) ch <- prometheus.MustNewConstMetric( c.responses, prometheus.CounterValue, - c.perfDataObject[0].UdpResponseSent, + data.UdpResponseSent, "udp", ) ch <- prometheus.MustNewConstMetric( c.unmatchedResponsesReceived, prometheus.CounterValue, - c.perfDataObject[0].UnmatchedResponsesReceived, + data.UnmatchedResponsesReceived, ) ch <- prometheus.MustNewConstMetric( c.winsQueries, prometheus.CounterValue, - c.perfDataObject[0].WinsLookupReceived, + data.WinsLookupReceived, "forward", ) ch <- prometheus.MustNewConstMetric( c.winsQueries, prometheus.CounterValue, - c.perfDataObject[0].WinsReverseLookupReceived, + data.WinsReverseLookupReceived, "reverse", ) ch <- prometheus.MustNewConstMetric( c.winsResponses, prometheus.CounterValue, - c.perfDataObject[0].WinsResponseSent, + data.WinsResponseSent, "forward", ) ch <- prometheus.MustNewConstMetric( c.winsResponses, prometheus.CounterValue, - c.perfDataObject[0].WinsReverseResponseSent, + data.WinsReverseResponseSent, "reverse", ) ch <- prometheus.MustNewConstMetric( c.secureUpdateFailures, prometheus.CounterValue, - c.perfDataObject[0].SecureUpdateFailure, + data.SecureUpdateFailure, ) ch <- prometheus.MustNewConstMetric( c.secureUpdateReceived, prometheus.CounterValue, - c.perfDataObject[0].SecureUpdateReceived, + data.SecureUpdateReceived, ) - - return nil } func (c *Collector) collectErrorStats(ch chan<- prometheus.Metric, maxScrapeDuration time.Duration) error { diff --git a/internal/collector/dns/dns_internal_test.go b/internal/collector/dns/dns_internal_test.go new file mode 100644 index 00000000..b699d740 --- /dev/null +++ b/internal/collector/dns/dns_internal_test.go @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build windows + +package dns + +import ( + "reflect" + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" +) + +func TestCollectPerfData(t *testing.T) { + t.Parallel() + + data := perfDataCounterValues{ + AxfrRequestSent: 1, + IxfrRequestSent: 2, + ZoneTransferSOARequestSent: 3, + AxfrSuccessReceived: 4, + IxfrTCPSuccessReceived: 5, + IxfrUDPSuccessReceived: 6, + DynamicUpdateQueued: 93, + RecursiveSendTimeOuts: 7, + } + + c := New(nil) + c.buildDescriptors() + + registry := prometheus.NewPedanticRegistry() + require.NoError(t, registry.Register(metricCollector{collector: c, data: data})) + + families, err := registry.Gather() + require.NoError(t, err) + + type sample struct { + metricType dto.MetricType + value float64 + } + + got := make(map[string]sample) + + for _, family := range families { + for _, metric := range family.GetMetric() { + labels := make([]string, 0, len(metric.GetLabel())) + for _, label := range metric.GetLabel() { + labels = append(labels, label.GetName()+"="+label.GetValue()) + } + + value := metric.GetGauge().GetValue() + if family.GetType() == dto.MetricType_COUNTER { + value = metric.GetCounter().GetValue() + } + + got[family.GetName()+"{"+strings.Join(labels, ",")+"}"] = sample{metricType: family.GetType(), value: value} + } + } + + for name, want := range map[string]sample{ + "windows_dns_zone_transfer_requests_sent_total{qtype=full}": {dto.MetricType_COUNTER, 1}, + "windows_dns_zone_transfer_requests_sent_total{qtype=incremental}": {dto.MetricType_COUNTER, 2}, + "windows_dns_zone_transfer_requests_sent_total{qtype=soa}": {dto.MetricType_COUNTER, 3}, + "windows_dns_zone_transfer_success_received_total{protocol=tcp,qtype=full}": {dto.MetricType_COUNTER, 4}, + "windows_dns_zone_transfer_success_received_total{protocol=tcp,qtype=incremental}": {dto.MetricType_COUNTER, 5}, + "windows_dns_zone_transfer_success_received_total{protocol=udp,qtype=incremental}": {dto.MetricType_COUNTER, 6}, + "windows_dns_dynamic_updates_queued_total{}": {dto.MetricType_COUNTER, 93}, + "windows_dns_recursive_query_send_timeouts_total{}": {dto.MetricType_COUNTER, 7}, + "windows_dns_memory_used_bytes{area=caching}": {dto.MetricType_GAUGE, 0}, + } { + require.Contains(t, got, name) + require.Equal(t, want, got[name], name) + } + + require.NotContains(t, got, "windows_dns_dynamic_updates_queued{}") +} + +// TestPerfDataCounterNames guards against a field reading a counter of a different name. +func TestPerfDataCounterNames(t *testing.T) { + t.Parallel() + + for field, counter := range map[string]string{ + "ZoneTransferSOARequestSent": "Zone Transfer SOA Request Sent", + "IxfrUDPSuccessReceived": "IXFR UDP Success Received", + "IxfrTCPSuccessReceived": "IXFR TCP Success Received", + "DynamicUpdateQueued": "Dynamic Update Queued", + "RecursiveSendTimeOuts": "Recursive Send TimeOuts", + } { + f, ok := reflect.TypeFor[perfDataCounterValues]().FieldByName(field) + require.True(t, ok, field) + require.Equal(t, counter, f.Tag.Get("perfdata"), field) + } +} + +type metricCollector struct { + collector *Collector + data perfDataCounterValues +} + +func (c metricCollector) Describe(ch chan<- *prometheus.Desc) { + prometheus.DescribeByCollect(c, ch) +} + +func (c metricCollector) Collect(ch chan<- prometheus.Metric) { + c.collector.collectPerfData(ch, c.data) +} diff --git a/internal/collector/dns/types.go b/internal/collector/dns/types.go index 7ddd69f3..8e2c2682 100644 --- a/internal/collector/dns/types.go +++ b/internal/collector/dns/types.go @@ -70,8 +70,8 @@ type perfDataCounterValues struct { _ float64 `perfdata:"Recursive Queries/sec"` RecursiveQueryFailure float64 `perfdata:"Recursive Query Failure"` _ float64 `perfdata:"Recursive Query Failure/sec"` - _ float64 `perfdata:"Recursive Send TimeOuts"` - RecursiveSendTimeOuts float64 `perfdata:"Recursive TimeOut/sec"` + RecursiveSendTimeOuts float64 `perfdata:"Recursive Send TimeOuts"` + _ float64 `perfdata:"Recursive TimeOut/sec"` _ float64 `perfdata:"Responses Suppressed"` _ float64 `perfdata:"Responses Suppressed/sec"` SecureUpdateFailure float64 `perfdata:"Secure Update Failure"` @@ -103,8 +103,8 @@ type perfDataCounterValues struct { WinsReverseResponseSent float64 `perfdata:"WINS Reverse Response Sent"` _ float64 `perfdata:"WINS Reverse Response Sent/sec"` ZoneTransferFailure float64 `perfdata:"Zone Transfer Failure"` - ZoneTransferSOARequestSent float64 `perfdata:"Zone Transfer Request Received"` - _ float64 `perfdata:"Zone Transfer SOA Request Sent"` + _ float64 `perfdata:"Zone Transfer Request Received"` + ZoneTransferSOARequestSent float64 `perfdata:"Zone Transfer SOA Request Sent"` _ float64 `perfdata:"Zone Transfer Success"` }