summaryrefslogtreecommitdiffstats
path: root/src/go/plugin/go.d/modules/nsd
diff options
context:
space:
mode:
Diffstat (limited to 'src/go/plugin/go.d/modules/nsd')
l---------src/go/plugin/go.d/modules/nsd/README.md1
-rw-r--r--src/go/plugin/go.d/modules/nsd/charts.go249
-rw-r--r--src/go/plugin/go.d/modules/nsd/collect.go81
-rw-r--r--src/go/plugin/go.d/modules/nsd/config_schema.json35
-rw-r--r--src/go/plugin/go.d/modules/nsd/exec.go47
-rw-r--r--src/go/plugin/go.d/modules/nsd/init.go23
-rw-r--r--src/go/plugin/go.d/modules/nsd/integrations/nsd.md203
-rw-r--r--src/go/plugin/go.d/modules/nsd/metadata.yaml272
-rw-r--r--src/go/plugin/go.d/modules/nsd/nsd.go97
-rw-r--r--src/go/plugin/go.d/modules/nsd/nsd_test.go337
-rw-r--r--src/go/plugin/go.d/modules/nsd/stats_counters.go123
-rw-r--r--src/go/plugin/go.d/modules/nsd/testdata/config.json4
-rw-r--r--src/go/plugin/go.d/modules/nsd/testdata/config.yaml2
-rw-r--r--src/go/plugin/go.d/modules/nsd/testdata/stats.txt95
14 files changed, 1569 insertions, 0 deletions
diff --git a/src/go/plugin/go.d/modules/nsd/README.md b/src/go/plugin/go.d/modules/nsd/README.md
new file mode 120000
index 000000000..a5cb8c98b
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/README.md
@@ -0,0 +1 @@
+integrations/nsd.md \ No newline at end of file
diff --git a/src/go/plugin/go.d/modules/nsd/charts.go b/src/go/plugin/go.d/modules/nsd/charts.go
new file mode 100644
index 000000000..aed4f3098
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/charts.go
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package nsd
+
+import (
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+)
+
+const (
+ prioQueries = module.Priority + iota
+ prioQueriesByType
+ prioQueriesByOpcode
+ prioQueriesByClass
+ prioQueriesByProtocol
+
+ prioAnswersByRcode
+
+ prioErrors
+
+ prioDrops
+
+ prioZones
+ prioZoneTransfersRequests
+ prioZoneTransferMemory
+
+ prioDatabaseSize
+
+ prioUptime
+)
+
+var charts = module.Charts{
+ queriesChart.Copy(),
+ queriesByTypeChart.Copy(),
+ queriesByOpcodeChart.Copy(),
+ queriesByClassChart.Copy(),
+ queriesByProtocolChart.Copy(),
+
+ answersByRcodeChart.Copy(),
+
+ zonesChart.Copy(),
+ zoneTransfersRequestsChart.Copy(),
+ zoneTransferMemoryChart.Copy(),
+
+ databaseSizeChart.Copy(),
+
+ errorsChart.Copy(),
+
+ dropsChart.Copy(),
+
+ uptimeChart.Copy(),
+}
+
+var (
+ queriesChart = module.Chart{
+ ID: "queries",
+ Title: "Queries",
+ Units: "queries/s",
+ Fam: "queries",
+ Ctx: "nsd.queries",
+ Priority: prioQueries,
+ Dims: module.Dims{
+ {ID: "num.queries", Name: "queries", Algo: module.Incremental},
+ },
+ }
+ queriesByTypeChart = func() module.Chart {
+ chart := module.Chart{
+ ID: "queries_by_type",
+ Title: "Queries Type",
+ Units: "queries/s",
+ Fam: "queries",
+ Ctx: "nsd.queries_by_type",
+ Priority: prioQueriesByType,
+ Type: module.Stacked,
+ }
+ for _, v := range queryTypes {
+ name := v
+ if s, ok := queryTypeNumberMap[v]; ok {
+ name = s
+ }
+ chart.Dims = append(chart.Dims, &module.Dim{
+ ID: "num.type." + v,
+ Name: name,
+ Algo: module.Incremental,
+ })
+ }
+ return chart
+ }()
+ queriesByOpcodeChart = func() module.Chart {
+ chart := module.Chart{
+ ID: "queries_by_opcode",
+ Title: "Queries Opcode",
+ Units: "queries/s",
+ Fam: "queries",
+ Ctx: "nsd.queries_by_opcode",
+ Priority: prioQueriesByOpcode,
+ Type: module.Stacked,
+ }
+ for _, v := range queryOpcodes {
+ chart.Dims = append(chart.Dims, &module.Dim{
+ ID: "num.opcode." + v,
+ Name: v,
+ Algo: module.Incremental,
+ })
+ }
+ return chart
+ }()
+ queriesByClassChart = func() module.Chart {
+ chart := module.Chart{
+ ID: "queries_by_class",
+ Title: "Queries Class",
+ Units: "queries/s",
+ Fam: "queries",
+ Ctx: "nsd.queries_by_class",
+ Priority: prioQueriesByClass,
+ Type: module.Stacked,
+ }
+ for _, v := range queryClasses {
+ chart.Dims = append(chart.Dims, &module.Dim{
+ ID: "num.class." + v,
+ Name: v,
+ Algo: module.Incremental,
+ })
+ }
+ return chart
+ }()
+ queriesByProtocolChart = module.Chart{
+ ID: "queries_by_protocol",
+ Title: "Queries Protocol",
+ Units: "queries/s",
+ Fam: "queries",
+ Ctx: "nsd.queries_by_protocol",
+ Priority: prioQueriesByProtocol,
+ Type: module.Stacked,
+ Dims: module.Dims{
+ {ID: "num.udp", Name: "udp", Algo: module.Incremental},
+ {ID: "num.udp6", Name: "udp6", Algo: module.Incremental},
+ {ID: "num.tcp", Name: "tcp", Algo: module.Incremental},
+ {ID: "num.tcp6", Name: "tcp6", Algo: module.Incremental},
+ {ID: "num.tls", Name: "tls", Algo: module.Incremental},
+ {ID: "num.tls6", Name: "tls6", Algo: module.Incremental},
+ },
+ }
+
+ answersByRcodeChart = func() module.Chart {
+ chart := module.Chart{
+ ID: "answers_by_rcode",
+ Title: "Answers Rcode",
+ Units: "answers/s",
+ Fam: "answers",
+ Ctx: "nsd.answers_by_rcode",
+ Priority: prioAnswersByRcode,
+ Type: module.Stacked,
+ }
+ for _, v := range answerRcodes {
+ chart.Dims = append(chart.Dims, &module.Dim{
+ ID: "num.rcode." + v,
+ Name: v,
+ Algo: module.Incremental,
+ })
+ }
+ return chart
+ }()
+
+ errorsChart = module.Chart{
+ ID: "errors",
+ Title: "Errors",
+ Units: "errors/s",
+ Fam: "errors",
+ Ctx: "nsd.errors",
+ Priority: prioErrors,
+ Dims: module.Dims{
+ {ID: "num.rxerr", Name: "query", Algo: module.Incremental},
+ {ID: "num.txerr", Name: "answer", Mul: -1, Algo: module.Incremental},
+ },
+ }
+
+ dropsChart = module.Chart{
+ ID: "drops",
+ Title: "Drops",
+ Units: "drops/s",
+ Fam: "drops",
+ Ctx: "nsd.drops",
+ Priority: prioDrops,
+ Dims: module.Dims{
+ {ID: "num.dropped", Name: "query", Algo: module.Incremental},
+ },
+ }
+
+ zonesChart = module.Chart{
+ ID: "zones",
+ Title: "Zones",
+ Units: "zones",
+ Fam: "zones",
+ Ctx: "nsd.zones",
+ Priority: prioZones,
+ Dims: module.Dims{
+ {ID: "zone.master", Name: "master"},
+ {ID: "zone.slave", Name: "slave"},
+ },
+ }
+ zoneTransfersRequestsChart = module.Chart{
+ ID: "zone_transfers_requests",
+ Title: "Zone Transfers",
+ Units: "requests/s",
+ Fam: "zones",
+ Ctx: "nsd.zone_transfers_requests",
+ Priority: prioZoneTransfersRequests,
+ Dims: module.Dims{
+ {ID: "num.raxfr", Name: "AXFR", Algo: module.Incremental},
+ {ID: "num.rixfr", Name: "IXFR", Algo: module.Incremental},
+ },
+ }
+ zoneTransferMemoryChart = module.Chart{
+ ID: "zone_transfer_memory",
+ Title: "Zone Transfer Memory",
+ Units: "bytes",
+ Fam: "zones",
+ Ctx: "nsd.zone_transfer_memory",
+ Priority: prioZoneTransferMemory,
+ Dims: module.Dims{
+ {ID: "size.xfrd.mem", Name: "used"},
+ },
+ }
+
+ databaseSizeChart = module.Chart{
+ ID: "database_size",
+ Title: "Database Size",
+ Units: "bytes",
+ Fam: "database",
+ Ctx: "nsd.database_size",
+ Priority: prioDatabaseSize,
+ Dims: module.Dims{
+ {ID: "size.db.disk", Name: "disk"},
+ {ID: "size.db.mem", Name: "mem"},
+ },
+ }
+
+ uptimeChart = module.Chart{
+ ID: "uptime",
+ Title: "Uptime",
+ Units: "seconds",
+ Fam: "uptime",
+ Ctx: "nsd.uptime",
+ Priority: prioUptime,
+ Dims: module.Dims{
+ {ID: "time.boot", Name: "uptime"},
+ },
+ }
+)
diff --git a/src/go/plugin/go.d/modules/nsd/collect.go b/src/go/plugin/go.d/modules/nsd/collect.go
new file mode 100644
index 000000000..d07341df3
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/collect.go
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package nsd
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "strconv"
+ "strings"
+)
+
+func (n *Nsd) collect() (map[string]int64, error) {
+ stats, err := n.exec.stats()
+ if err != nil {
+ return nil, err
+ }
+
+ if len(stats) == 0 {
+ return nil, errors.New("empty stats response")
+ }
+
+ mx := make(map[string]int64)
+
+ sc := bufio.NewScanner(bytes.NewReader(stats))
+
+ for sc.Scan() {
+ n.collectStatsLine(mx, sc.Text())
+ }
+
+ if len(mx) == 0 {
+ return nil, errors.New("unexpected stats response: no metrics found")
+ }
+
+ addMissingMetrics(mx, "num.rcode.", answerRcodes)
+ addMissingMetrics(mx, "num.opcode.", queryOpcodes)
+ addMissingMetrics(mx, "num.class.", queryClasses)
+ addMissingMetrics(mx, "num.type.", queryTypes)
+
+ return mx, nil
+}
+
+func (n *Nsd) collectStatsLine(mx map[string]int64, line string) {
+ if line = strings.TrimSpace(line); line == "" {
+ return
+ }
+
+ key, value, ok := strings.Cut(line, "=")
+ if !ok {
+ n.Debugf("invalid line in stats: '%s'", line)
+ return
+ }
+
+ var v int64
+ var f float64
+ var err error
+
+ switch key {
+ case "time.boot":
+ f, err = strconv.ParseFloat(value, 64)
+ v = int64(f)
+ default:
+ v, err = strconv.ParseInt(value, 10, 64)
+ }
+
+ if err != nil {
+ n.Debugf("invalid value in stats line '%s': '%s'", line, value)
+ return
+ }
+
+ mx[key] = v
+}
+
+func addMissingMetrics(mx map[string]int64, prefix string, values []string) {
+ for _, v := range values {
+ k := prefix + v
+ if _, ok := mx[k]; !ok {
+ mx[k] = 0
+ }
+ }
+}
diff --git a/src/go/plugin/go.d/modules/nsd/config_schema.json b/src/go/plugin/go.d/modules/nsd/config_schema.json
new file mode 100644
index 000000000..d49107c71
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/config_schema.json
@@ -0,0 +1,35 @@
+{
+ "jsonSchema": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "NSD collector configuration.",
+ "type": "object",
+ "properties": {
+ "update_every": {
+ "title": "Update every",
+ "description": "Data collection interval, measured in seconds.",
+ "type": "integer",
+ "minimum": 1,
+ "default": 10
+ },
+ "timeout": {
+ "title": "Timeout",
+ "description": "Timeout for executing the binary, specified in seconds.",
+ "type": "number",
+ "minimum": 0.5,
+ "default": 2
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": {
+ "^name$": {}
+ }
+ },
+ "uiSchema": {
+ "uiOptions": {
+ "fullPage": true
+ },
+ "timeout": {
+ "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
+ }
+ }
+}
diff --git a/src/go/plugin/go.d/modules/nsd/exec.go b/src/go/plugin/go.d/modules/nsd/exec.go
new file mode 100644
index 000000000..b05082f3c
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/exec.go
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package nsd
+
+import (
+ "context"
+ "fmt"
+ "os/exec"
+ "time"
+
+ "github.com/netdata/netdata/go/plugins/logger"
+)
+
+type nsdControlBinary interface {
+ stats() ([]byte, error)
+}
+
+func newNsdControlExec(ndsudoPath string, timeout time.Duration, log *logger.Logger) *nsdControlExec {
+ return &nsdControlExec{
+ Logger: log,
+ ndsudoPath: ndsudoPath,
+ timeout: timeout,
+ }
+}
+
+type nsdControlExec struct {
+ *logger.Logger
+
+ ndsudoPath string
+ timeout time.Duration
+}
+
+func (e *nsdControlExec) stats() ([]byte, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), e.timeout)
+ defer cancel()
+
+ cmd := exec.CommandContext(ctx, e.ndsudoPath, "nsd-control-stats")
+
+ e.Debugf("executing '%s'", cmd)
+
+ bs, err := cmd.Output()
+ if err != nil {
+ return nil, fmt.Errorf("error on '%s': %v", cmd, err)
+ }
+
+ return bs, nil
+}
diff --git a/src/go/plugin/go.d/modules/nsd/init.go b/src/go/plugin/go.d/modules/nsd/init.go
new file mode 100644
index 000000000..63843caba
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/init.go
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package nsd
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/netdata/netdata/go/plugins/pkg/executable"
+)
+
+func (n *Nsd) initNsdControlExec() (nsdControlBinary, error) {
+ ndsudoPath := filepath.Join(executable.Directory, "ndsudo")
+ if _, err := os.Stat(ndsudoPath); err != nil {
+ return nil, fmt.Errorf("ndsudo executable not found: %v", err)
+
+ }
+
+ nsdControl := newNsdControlExec(ndsudoPath, n.Timeout.Duration(), n.Logger)
+
+ return nsdControl, nil
+}
diff --git a/src/go/plugin/go.d/modules/nsd/integrations/nsd.md b/src/go/plugin/go.d/modules/nsd/integrations/nsd.md
new file mode 100644
index 000000000..745b872d7
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/integrations/nsd.md
@@ -0,0 +1,203 @@
+<!--startmeta
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/nsd/README.md"
+meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/nsd/metadata.yaml"
+sidebar_label: "NSD"
+learn_status: "Published"
+learn_rel_path: "Collecting Metrics/DNS and DHCP Servers"
+most_popular: False
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
+endmeta-->
+
+# NSD
+
+
+<img src="https://netdata.cloud/img/nsd.svg" width="150"/>
+
+
+Plugin: go.d.plugin
+Module: nsd
+
+<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />
+
+## Overview
+
+This collector monitors NSD statistics like queries, zones, protocols, query types and more. It relies on the [`nsd-control`](https://nsd.docs.nlnetlabs.nl/en/latest/manpages/nsd-control.html) CLI tool but avoids directly executing the binary. Instead, it utilizes `ndsudo`, a Netdata helper specifically designed to run privileged commands securely within the Netdata environment. This approach eliminates the need to use `sudo`, improving security and potentially simplifying permission management.
+Executed commands:
+- `nsd-control stats_noreset`
+
+
+
+
+This collector is supported on all platforms.
+
+This collector only supports collecting metrics from a single instance of this integration.
+
+
+### Default Behavior
+
+#### Auto-Detection
+
+This integration doesn't support auto-detection.
+
+#### Limits
+
+The default configuration for this integration does not impose any limits on data collection.
+
+#### Performance Impact
+
+The default configuration for this integration is not expected to impose a significant performance impact on the system.
+
+
+## Metrics
+
+Metrics grouped by *scope*.
+
+The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels.
+
+
+
+### Per NSD instance
+
+These metrics refer to the the entire monitored application.
+
+This scope has no labels.
+
+Metrics:
+
+| Metric | Dimensions | Unit |
+|:------|:----------|:----|
+| nsd.queries | queries | queries/s |
+| nsd.queries_by_type | A, NS, MD, MF, CNAME, SOA, MB, MG, MR, NULL, WKS, PTR, HINFO, MINFO, MX, TXT, RP, AFSDB, X25, ISDN, RT, NSAP, SIG, KEY, PX, AAAA, LOC, NXT, SRV, NAPTR, KX, CERT, DNAME, OPT, APL, DS, SSHFP, IPSECKEY, RRSIG, NSEC, DNSKEY, DHCID, NSEC3, NSEC3PARAM, TLSA, SMIMEA, CDS, CDNSKEY, OPENPGPKEY, CSYNC, ZONEMD, SVCB, HTTPS, SPF, NID, L32, L64, LP, EUI48, EUI64, URI, CAA, AVC, DLV, IXFR, AXFR, MAILB, MAILA, ANY | queries/s |
+| nsd.queries_by_opcode | QUERY, IQUERY, STATUS, NOTIFY, UPDATE, OTHER | queries/s |
+| nsd.queries_by_class | IN, CS, CH, HS | queries/s |
+| nsd.queries_by_protocol | udp, udp6, tcp, tcp6, tls, tls6 | queries/s |
+| nsd.answers_by_rcode | NOERROR, FORMERR, SERVFAIL, NXDOMAIN, NOTIMP, REFUSED, YXDOMAIN, YXRRSET, NXRRSET, NOTAUTH, NOTZONE, RCODE11, RCODE12, RCODE13, RCODE14, RCODE15, BADVERS | answers/s |
+| nsd.errors | query, answer | errors/s |
+| nsd.drops | query | drops/s |
+| nsd.zones | master, slave | zones |
+| nsd.zone_transfers_requests | AXFR, IXFR | requests/s |
+| nsd.zone_transfer_memory | used | bytes |
+| nsd.database_size | disk, mem | bytes |
+| nsd.uptime | uptime | seconds |
+
+
+
+## Alerts
+
+There are no alerts configured by default for this integration.
+
+
+## Setup
+
+### Prerequisites
+
+No action required.
+
+### Configuration
+
+#### File
+
+The configuration file name for this integration is `go.d/nsd.conf`.
+
+
+You can edit the configuration file using the `edit-config` script from the
+Netdata [config directory](/docs/netdata-agent/configuration/README.md#the-netdata-config-directory).
+
+```bash
+cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata
+sudo ./edit-config go.d/nsd.conf
+```
+#### Options
+
+The following options can be defined globally: update_every.
+
+
+<details open><summary>Config options</summary>
+
+| Name | Description | Default | Required |
+|:----|:-----------|:-------|:--------:|
+| update_every | Data collection frequency. | 10 | no |
+| timeout | nsd-control binary execution timeout. | 2 | no |
+
+</details>
+
+#### Examples
+
+##### Custom update_every
+
+Allows you to override the default data collection interval.
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: nsd
+ update_every: 5 # Collect logical volume statistics every 5 seconds
+
+```
+</details>
+
+
+
+## Troubleshooting
+
+### Debug Mode
+
+**Important**: Debug mode is not supported for data collection jobs created via the UI using the Dyncfg feature.
+
+To troubleshoot issues with the `nsd` collector, run the `go.d.plugin` with the debug option enabled. The output
+should give you clues as to why the collector isn't working.
+
+- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on
+ your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`.
+
+ ```bash
+ cd /usr/libexec/netdata/plugins.d/
+ ```
+
+- Switch to the `netdata` user.
+
+ ```bash
+ sudo -u netdata -s
+ ```
+
+- Run the `go.d.plugin` to debug the collector:
+
+ ```bash
+ ./go.d.plugin -d -m nsd
+ ```
+
+### Getting Logs
+
+If you're encountering problems with the `nsd` collector, follow these steps to retrieve logs and identify potential issues:
+
+- **Run the command** specific to your system (systemd, non-systemd, or Docker container).
+- **Examine the output** for any warnings or error messages that might indicate issues. These messages should provide clues about the root cause of the problem.
+
+#### System with systemd
+
+Use the following command to view logs generated since the last Netdata service restart:
+
+```bash
+journalctl _SYSTEMD_INVOCATION_ID="$(systemctl show --value --property=InvocationID netdata)" --namespace=netdata --grep nsd
+```
+
+#### System without systemd
+
+Locate the collector log file, typically at `/var/log/netdata/collector.log`, and use `grep` to filter for collector's name:
+
+```bash
+grep nsd /var/log/netdata/collector.log
+```
+
+**Note**: This method shows logs from all restarts. Focus on the **latest entries** for troubleshooting current issues.
+
+#### Docker Container
+
+If your Netdata runs in a Docker container named "netdata" (replace if different), use this command:
+
+```bash
+docker logs netdata 2>&1 | grep nsd
+```
+
+
diff --git a/src/go/plugin/go.d/modules/nsd/metadata.yaml b/src/go/plugin/go.d/modules/nsd/metadata.yaml
new file mode 100644
index 000000000..a31aa38af
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/metadata.yaml
@@ -0,0 +1,272 @@
+plugin_name: go.d.plugin
+modules:
+ - meta:
+ id: collector-go.d.plugin-nsd
+ plugin_name: go.d.plugin
+ module_name: nsd
+ monitored_instance:
+ name: NSD
+ link: "https://nsd.docs.nlnetlabs.nl/en/latest"
+ icon_filename: 'nsd.svg'
+ categories:
+ - data-collection.dns-and-dhcp-servers
+ keywords:
+ - nsd
+ - dns
+ related_resources:
+ integrations:
+ list: []
+ info_provided_to_referring_integrations:
+ description: ""
+ most_popular: false
+ overview:
+ data_collection:
+ metrics_description: >
+ This collector monitors NSD statistics like queries, zones, protocols, query types and more.
+ It relies on the [`nsd-control`](https://nsd.docs.nlnetlabs.nl/en/latest/manpages/nsd-control.html) CLI tool but avoids directly executing the binary.
+ Instead, it utilizes `ndsudo`, a Netdata helper specifically designed to run privileged commands securely within the Netdata environment.
+ This approach eliminates the need to use `sudo`, improving security and potentially simplifying permission management.
+
+ Executed commands:
+
+ - `nsd-control stats_noreset`
+ method_description: ""
+ supported_platforms:
+ include: []
+ exclude: []
+ multi_instance: false
+ additional_permissions:
+ description: ""
+ default_behavior:
+ auto_detection:
+ description: ""
+ limits:
+ description: ""
+ performance_impact:
+ description: ""
+ setup:
+ prerequisites:
+ list: []
+ configuration:
+ file:
+ name: go.d/nsd.conf
+ options:
+ description: |
+ The following options can be defined globally: update_every.
+ folding:
+ title: Config options
+ enabled: true
+ list:
+ - name: update_every
+ description: Data collection frequency.
+ default_value: 10
+ required: false
+ - name: timeout
+ description: nsd-control binary execution timeout.
+ default_value: 2
+ required: false
+ examples:
+ folding:
+ title: Config
+ enabled: true
+ list:
+ - name: Custom update_every
+ description: Allows you to override the default data collection interval.
+ config: |
+ jobs:
+ - name: nsd
+ update_every: 5 # Collect logical volume statistics every 5 seconds
+ troubleshooting:
+ problems:
+ list: []
+ alerts: []
+ metrics:
+ folding:
+ title: Metrics
+ enabled: false
+ description: ""
+ availability: []
+ scopes:
+ - name: global
+ description: These metrics refer to the the entire monitored application.
+ labels: []
+ metrics:
+ - name: nsd.queries
+ description: Queries
+ unit: 'queries/s'
+ chart_type: line
+ dimensions:
+ - name: queries
+ - name: nsd.queries_by_type
+ description: Queries Type
+ unit: 'queries/s'
+ chart_type: stacked
+ dimensions:
+ - name: "A"
+ - name: "NS"
+ - name: "MD"
+ - name: "MF"
+ - name: "CNAME"
+ - name: "SOA"
+ - name: "MB"
+ - name: "MG"
+ - name: "MR"
+ - name: "NULL"
+ - name: "WKS"
+ - name: "PTR"
+ - name: "HINFO"
+ - name: "MINFO"
+ - name: "MX"
+ - name: "TXT"
+ - name: "RP"
+ - name: "AFSDB"
+ - name: "X25"
+ - name: "ISDN"
+ - name: "RT"
+ - name: "NSAP"
+ - name: "SIG"
+ - name: "KEY"
+ - name: "PX"
+ - name: "AAAA"
+ - name: "LOC"
+ - name: "NXT"
+ - name: "SRV"
+ - name: "NAPTR"
+ - name: "KX"
+ - name: "CERT"
+ - name: "DNAME"
+ - name: "OPT"
+ - name: "APL"
+ - name: "DS"
+ - name: "SSHFP"
+ - name: "IPSECKEY"
+ - name: "RRSIG"
+ - name: "NSEC"
+ - name: "DNSKEY"
+ - name: "DHCID"
+ - name: "NSEC3"
+ - name: "NSEC3PARAM"
+ - name: "TLSA"
+ - name: "SMIMEA"
+ - name: "CDS"
+ - name: "CDNSKEY"
+ - name: "OPENPGPKEY"
+ - name: "CSYNC"
+ - name: "ZONEMD"
+ - name: "SVCB"
+ - name: "HTTPS"
+ - name: "SPF"
+ - name: "NID"
+ - name: "L32"
+ - name: "L64"
+ - name: "LP"
+ - name: "EUI48"
+ - name: "EUI64"
+ - name: "URI"
+ - name: "CAA"
+ - name: "AVC"
+ - name: "DLV"
+ - name: "IXFR"
+ - name: "AXFR"
+ - name: "MAILB"
+ - name: "MAILA"
+ - name: "ANY"
+ - name: nsd.queries_by_opcode
+ description: Queries Opcode
+ unit: 'queries/s'
+ chart_type: stacked
+ dimensions:
+ - name: "QUERY"
+ - name: "IQUERY"
+ - name: "STATUS"
+ - name: "NOTIFY"
+ - name: "UPDATE"
+ - name: "OTHER"
+ - name: nsd.queries_by_class
+ description: Queries Class
+ unit: 'queries/s'
+ chart_type: stacked
+ dimensions:
+ - name: "IN"
+ - name: "CS"
+ - name: "CH"
+ - name: "HS"
+ - name: nsd.queries_by_protocol
+ description: Queries Protocol
+ unit: 'queries/s'
+ chart_type: stacked
+ dimensions:
+ - name: "udp"
+ - name: "udp6"
+ - name: "tcp"
+ - name: "tcp6"
+ - name: "tls"
+ - name: "tls6"
+ - name: nsd.answers_by_rcode
+ description: Answers Rcode
+ unit: 'answers/s'
+ chart_type: stacked
+ dimensions:
+ - name: "NOERROR"
+ - name: "FORMERR"
+ - name: "SERVFAIL"
+ - name: "NXDOMAIN"
+ - name: "NOTIMP"
+ - name: "REFUSED"
+ - name: "YXDOMAIN"
+ - name: "YXRRSET"
+ - name: "NXRRSET"
+ - name: "NOTAUTH"
+ - name: "NOTZONE"
+ - name: "RCODE11"
+ - name: "RCODE12"
+ - name: "RCODE13"
+ - name: "RCODE14"
+ - name: "RCODE15"
+ - name: "BADVERS"
+ - name: nsd.errors
+ description: Errors
+ unit: 'errors/s'
+ chart_type: line
+ dimensions:
+ - name: "query"
+ - name: "answer"
+ - name: nsd.drops
+ description: Drops
+ unit: 'drops/s'
+ chart_type: line
+ dimensions:
+ - name: "query"
+ - name: nsd.zones
+ description: Zones
+ unit: 'zones'
+ chart_type: line
+ dimensions:
+ - name: "master"
+ - name: "slave"
+ - name: nsd.zone_transfers_requests
+ description: Zone Transfers
+ unit: 'requests/s'
+ chart_type: line
+ dimensions:
+ - name: "AXFR"
+ - name: "IXFR"
+ - name: nsd.zone_transfer_memory
+ description: Zone Transfer Memory
+ unit: 'bytes'
+ chart_type: line
+ dimensions:
+ - name: "used"
+ - name: nsd.database_size
+ description: Database Size
+ unit: 'bytes'
+ chart_type: line
+ dimensions:
+ - name: "disk"
+ - name: "mem"
+ - name: nsd.uptime
+ description: Uptime
+ unit: 'seconds'
+ chart_type: line
+ dimensions:
+ - name: "uptime"
diff --git a/src/go/plugin/go.d/modules/nsd/nsd.go b/src/go/plugin/go.d/modules/nsd/nsd.go
new file mode 100644
index 000000000..fae0f67f3
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/nsd.go
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package nsd
+
+import (
+ _ "embed"
+ "errors"
+ "time"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
+)
+
+//go:embed "config_schema.json"
+var configSchema string
+
+func init() {
+ module.Register("nsd", module.Creator{
+ JobConfigSchema: configSchema,
+ Defaults: module.Defaults{
+ UpdateEvery: 10,
+ },
+ Create: func() module.Module { return New() },
+ Config: func() any { return &Config{} },
+ })
+}
+
+func New() *Nsd {
+ return &Nsd{
+ Config: Config{
+ Timeout: web.Duration(time.Second * 2),
+ },
+ charts: charts.Copy(),
+ }
+}
+
+type Config struct {
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
+ Timeout web.Duration `yaml:"timeout,omitempty" json:"timeout"`
+}
+
+type Nsd struct {
+ module.Base
+ Config `yaml:",inline" json:""`
+
+ charts *module.Charts
+
+ exec nsdControlBinary
+}
+
+func (n *Nsd) Configuration() any {
+ return n.Config
+}
+
+func (n *Nsd) Init() error {
+ nsdControl, err := n.initNsdControlExec()
+ if err != nil {
+ n.Errorf("nsd-control exec initialization: %v", err)
+ return err
+ }
+ n.exec = nsdControl
+
+ return nil
+}
+
+func (n *Nsd) Check() error {
+ mx, err := n.collect()
+ if err != nil {
+ n.Error(err)
+ return err
+ }
+
+ if len(mx) == 0 {
+ return errors.New("no metrics collected")
+ }
+
+ return nil
+}
+
+func (n *Nsd) Charts() *module.Charts {
+ return n.charts
+}
+
+func (n *Nsd) Collect() map[string]int64 {
+ mx, err := n.collect()
+ if err != nil {
+ n.Error(err)
+ }
+
+ if len(mx) == 0 {
+ return nil
+ }
+
+ return mx
+}
+
+func (n *Nsd) Cleanup() {}
diff --git a/src/go/plugin/go.d/modules/nsd/nsd_test.go b/src/go/plugin/go.d/modules/nsd/nsd_test.go
new file mode 100644
index 000000000..24f38b512
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/nsd_test.go
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package nsd
+
+import (
+ "errors"
+ "os"
+ "testing"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+var (
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
+
+ dataStats, _ = os.ReadFile("testdata/stats.txt")
+)
+
+func Test_testDataIsValid(t *testing.T) {
+ for name, data := range map[string][]byte{
+ "dataConfigJSON": dataConfigJSON,
+ "dataConfigYAML": dataConfigYAML,
+ "dataStats": dataStats,
+ } {
+ require.NotNil(t, data, name)
+
+ }
+}
+
+func TestNsd_Configuration(t *testing.T) {
+ module.TestConfigurationSerialize(t, &Nsd{}, dataConfigJSON, dataConfigYAML)
+}
+
+func TestNsd_Init(t *testing.T) {
+ tests := map[string]struct {
+ config Config
+ wantFail bool
+ }{
+ "fails if failed to locate ndsudo": {
+ wantFail: true,
+ config: New().Config,
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ nsd := New()
+ nsd.Config = test.config
+
+ if test.wantFail {
+ assert.Error(t, nsd.Init())
+ } else {
+ assert.NoError(t, nsd.Init())
+ }
+ })
+ }
+}
+
+func TestNsd_Cleanup(t *testing.T) {
+ tests := map[string]struct {
+ prepare func() *Nsd
+ }{
+ "not initialized exec": {
+ prepare: func() *Nsd {
+ return New()
+ },
+ },
+ "after check": {
+ prepare: func() *Nsd {
+ nsd := New()
+ nsd.exec = prepareMockOK()
+ _ = nsd.Check()
+ return nsd
+ },
+ },
+ "after collect": {
+ prepare: func() *Nsd {
+ nsd := New()
+ nsd.exec = prepareMockOK()
+ _ = nsd.Collect()
+ return nsd
+ },
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ nsd := test.prepare()
+
+ assert.NotPanics(t, nsd.Cleanup)
+ })
+ }
+}
+
+func TestNsd_Charts(t *testing.T) {
+ assert.NotNil(t, New().Charts())
+}
+
+func TestNsd_Check(t *testing.T) {
+ tests := map[string]struct {
+ prepareMock func() *mockNsdControl
+ wantFail bool
+ }{
+ "success case": {
+ prepareMock: prepareMockOK,
+ wantFail: false,
+ },
+ "error on stats call": {
+ prepareMock: prepareMockErrOnStats,
+ wantFail: true,
+ },
+ "empty response": {
+ prepareMock: prepareMockEmptyResponse,
+ wantFail: true,
+ },
+ "unexpected response": {
+ prepareMock: prepareMockUnexpectedResponse,
+ wantFail: true,
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ nsd := New()
+ mock := test.prepareMock()
+ nsd.exec = mock
+
+ if test.wantFail {
+ assert.Error(t, nsd.Check())
+ } else {
+ assert.NoError(t, nsd.Check())
+ }
+ })
+ }
+}
+
+func TestNsd_Collect(t *testing.T) {
+ tests := map[string]struct {
+ prepareMock func() *mockNsdControl
+ wantMetrics map[string]int64
+ }{
+ "success case": {
+ prepareMock: prepareMockOK,
+ wantMetrics: map[string]int64{
+ "num.answer_wo_aa": 1,
+ "num.class.CH": 0,
+ "num.class.CS": 0,
+ "num.class.HS": 0,
+ "num.class.IN": 1,
+ "num.dropped": 1,
+ "num.edns": 1,
+ "num.ednserr": 1,
+ "num.opcode.IQUERY": 0,
+ "num.opcode.NOTIFY": 0,
+ "num.opcode.OTHER": 0,
+ "num.opcode.QUERY": 1,
+ "num.opcode.STATUS": 0,
+ "num.opcode.UPDATE": 0,
+ "num.queries": 1,
+ "num.raxfr": 1,
+ "num.rcode.BADVERS": 0,
+ "num.rcode.FORMERR": 1,
+ "num.rcode.NOERROR": 1,
+ "num.rcode.NOTAUTH": 0,
+ "num.rcode.NOTIMP": 1,
+ "num.rcode.NOTZONE": 0,
+ "num.rcode.NXDOMAIN": 1,
+ "num.rcode.NXRRSET": 0,
+ "num.rcode.RCODE11": 0,
+ "num.rcode.RCODE12": 0,
+ "num.rcode.RCODE13": 0,
+ "num.rcode.RCODE14": 0,
+ "num.rcode.RCODE15": 0,
+ "num.rcode.REFUSED": 1,
+ "num.rcode.SERVFAIL": 1,
+ "num.rcode.YXDOMAIN": 1,
+ "num.rcode.YXRRSET": 0,
+ "num.rixfr": 1,
+ "num.rxerr": 1,
+ "num.tcp": 1,
+ "num.tcp6": 1,
+ "num.tls": 1,
+ "num.tls6": 1,
+ "num.truncated": 1,
+ "num.txerr": 1,
+ "num.type.A": 1,
+ "num.type.AAAA": 1,
+ "num.type.AFSDB": 1,
+ "num.type.APL": 1,
+ "num.type.AVC": 0,
+ "num.type.CAA": 0,
+ "num.type.CDNSKEY": 1,
+ "num.type.CDS": 1,
+ "num.type.CERT": 1,
+ "num.type.CNAME": 1,
+ "num.type.CSYNC": 1,
+ "num.type.DHCID": 1,
+ "num.type.DLV": 0,
+ "num.type.DNAME": 1,
+ "num.type.DNSKEY": 1,
+ "num.type.DS": 1,
+ "num.type.EUI48": 1,
+ "num.type.EUI64": 1,
+ "num.type.HINFO": 1,
+ "num.type.HTTPS": 1,
+ "num.type.IPSECKEY": 1,
+ "num.type.ISDN": 1,
+ "num.type.KEY": 1,
+ "num.type.KX": 1,
+ "num.type.L32": 1,
+ "num.type.L64": 1,
+ "num.type.LOC": 1,
+ "num.type.LP": 1,
+ "num.type.MB": 1,
+ "num.type.MD": 1,
+ "num.type.MF": 1,
+ "num.type.MG": 1,
+ "num.type.MINFO": 1,
+ "num.type.MR": 1,
+ "num.type.MX": 1,
+ "num.type.NAPTR": 1,
+ "num.type.NID": 1,
+ "num.type.NS": 1,
+ "num.type.NSAP": 1,
+ "num.type.NSEC": 1,
+ "num.type.NSEC3": 1,
+ "num.type.NSEC3PARAM": 1,
+ "num.type.NULL": 1,
+ "num.type.NXT": 1,
+ "num.type.OPENPGPKEY": 1,
+ "num.type.OPT": 1,
+ "num.type.PTR": 1,
+ "num.type.PX": 1,
+ "num.type.RP": 1,
+ "num.type.RRSIG": 1,
+ "num.type.RT": 1,
+ "num.type.SIG": 1,
+ "num.type.SMIMEA": 1,
+ "num.type.SOA": 1,
+ "num.type.SPF": 1,
+ "num.type.SRV": 1,
+ "num.type.SSHFP": 1,
+ "num.type.SVCB": 1,
+ "num.type.TLSA": 1,
+ "num.type.TXT": 1,
+ "num.type.TYPE252": 0,
+ "num.type.TYPE255": 0,
+ "num.type.URI": 0,
+ "num.type.WKS": 1,
+ "num.type.X25": 1,
+ "num.type.ZONEMD": 1,
+ "num.udp": 1,
+ "num.udp6": 1,
+ "server0.queries": 1,
+ "size.config.disk": 1,
+ "size.config.mem": 1064,
+ "size.db.disk": 576,
+ "size.db.mem": 920,
+ "size.xfrd.mem": 1160464,
+ "time.boot": 556,
+ "zone.master": 1,
+ "zone.slave": 1,
+ },
+ },
+ "error on lvs report call": {
+ prepareMock: prepareMockErrOnStats,
+ wantMetrics: nil,
+ },
+ "empty response": {
+ prepareMock: prepareMockEmptyResponse,
+ wantMetrics: nil,
+ },
+ "unexpected response": {
+ prepareMock: prepareMockUnexpectedResponse,
+ wantMetrics: nil,
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ nsd := New()
+ mock := test.prepareMock()
+ nsd.exec = mock
+
+ mx := nsd.Collect()
+
+ assert.Equal(t, test.wantMetrics, mx)
+
+ if len(test.wantMetrics) > 0 {
+ assert.Len(t, *nsd.Charts(), len(charts))
+ module.TestMetricsHasAllChartsDims(t, nsd.Charts(), mx)
+ }
+ })
+ }
+}
+
+func prepareMockOK() *mockNsdControl {
+ return &mockNsdControl{
+ dataStats: dataStats,
+ }
+}
+
+func prepareMockErrOnStats() *mockNsdControl {
+ return &mockNsdControl{
+ errOnStatus: true,
+ }
+}
+
+func prepareMockEmptyResponse() *mockNsdControl {
+ return &mockNsdControl{}
+}
+
+func prepareMockUnexpectedResponse() *mockNsdControl {
+ return &mockNsdControl{
+ dataStats: []byte(`
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+Nulla malesuada erat id magna mattis, eu viverra tellus rhoncus.
+Fusce et felis pulvinar, posuere sem non, porttitor eros.
+`),
+ }
+}
+
+type mockNsdControl struct {
+ errOnStatus bool
+ dataStats []byte
+}
+
+func (m *mockNsdControl) stats() ([]byte, error) {
+ if m.errOnStatus {
+ return nil, errors.New("mock.status() error")
+ }
+ return m.dataStats, nil
+}
diff --git a/src/go/plugin/go.d/modules/nsd/stats_counters.go b/src/go/plugin/go.d/modules/nsd/stats_counters.go
new file mode 100644
index 000000000..8ebe706a5
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/stats_counters.go
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package nsd
+
+// Docs: https://nsd.docs.nlnetlabs.nl/en/latest/manpages/nsd-control.html?highlight=elapsed#statistics-counters
+// Source: https://github.com/NLnetLabs/nsd/blob/b4a5ccd2235a1f8f71f7c640390e409bf123c963/remote.c#L2735
+
+// https://github.com/NLnetLabs/nsd/blob/b4a5ccd2235a1f8f71f7c640390e409bf123c963/remote.c#L2737
+var answerRcodes = []string{
+ "NOERROR",
+ "FORMERR",
+ "SERVFAIL",
+ "NXDOMAIN",
+ "NOTIMP",
+ "REFUSED",
+ "YXDOMAIN",
+ "YXRRSET",
+ "NXRRSET",
+ "NOTAUTH",
+ "NOTZONE",
+ "RCODE11",
+ "RCODE12",
+ "RCODE13",
+ "RCODE14",
+ "RCODE15",
+ "BADVERS",
+}
+
+// https://github.com/NLnetLabs/nsd/blob/b4a5ccd2235a1f8f71f7c640390e409bf123c963/remote.c#L2706
+var queryOpcodes = []string{
+ "QUERY",
+ "IQUERY",
+ "STATUS",
+ "NOTIFY",
+ "UPDATE",
+ "OTHER",
+}
+
+// https://github.com/NLnetLabs/nsd/blob/b4a5ccd2235a1f8f71f7c640390e409bf123c963/dns.c#L27
+var queryClasses = []string{
+ "IN",
+ "CS",
+ "CH",
+ "HS",
+}
+
+// https://github.com/NLnetLabs/nsd/blob/b4a5ccd2235a1f8f71f7c640390e409bf123c963/dns.c#L35
+var queryTypes = []string{
+ "A",
+ "NS",
+ "MD",
+ "MF",
+ "CNAME",
+ "SOA",
+ "MB",
+ "MG",
+ "MR",
+ "NULL",
+ "WKS",
+ "PTR",
+ "HINFO",
+ "MINFO",
+ "MX",
+ "TXT",
+ "RP",
+ "AFSDB",
+ "X25",
+ "ISDN",
+ "RT",
+ "NSAP",
+ "SIG",
+ "KEY",
+ "PX",
+ "AAAA",
+ "LOC",
+ "NXT",
+ "SRV",
+ "NAPTR",
+ "KX",
+ "CERT",
+ "DNAME",
+ "OPT",
+ "APL",
+ "DS",
+ "SSHFP",
+ "IPSECKEY",
+ "RRSIG",
+ "NSEC",
+ "DNSKEY",
+ "DHCID",
+ "NSEC3",
+ "NSEC3PARAM",
+ "TLSA",
+ "SMIMEA",
+ "CDS",
+ "CDNSKEY",
+ "OPENPGPKEY",
+ "CSYNC",
+ "ZONEMD",
+ "SVCB",
+ "HTTPS",
+ "SPF",
+ "NID",
+ "L32",
+ "L64",
+ "LP",
+ "EUI48",
+ "EUI64",
+ "URI",
+ "CAA",
+ "AVC",
+ "DLV",
+ "TYPE252",
+ "TYPE255",
+}
+
+var queryTypeNumberMap = map[string]string{
+ "TYPE251": "IXFR",
+ "TYPE252": "AXFR",
+ "TYPE253": "MAILB",
+ "TYPE254": "MAILA",
+ "TYPE255": "ANY",
+}
diff --git a/src/go/plugin/go.d/modules/nsd/testdata/config.json b/src/go/plugin/go.d/modules/nsd/testdata/config.json
new file mode 100644
index 000000000..291ecee3d
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/testdata/config.json
@@ -0,0 +1,4 @@
+{
+ "update_every": 123,
+ "timeout": 123.123
+}
diff --git a/src/go/plugin/go.d/modules/nsd/testdata/config.yaml b/src/go/plugin/go.d/modules/nsd/testdata/config.yaml
new file mode 100644
index 000000000..25b0b4c78
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/testdata/config.yaml
@@ -0,0 +1,2 @@
+update_every: 123
+timeout: 123.123
diff --git a/src/go/plugin/go.d/modules/nsd/testdata/stats.txt b/src/go/plugin/go.d/modules/nsd/testdata/stats.txt
new file mode 100644
index 000000000..cb6d8b829
--- /dev/null
+++ b/src/go/plugin/go.d/modules/nsd/testdata/stats.txt
@@ -0,0 +1,95 @@
+server0.queries=1
+num.queries=1
+time.boot=556.488415
+time.elapsed=556.488415
+size.db.disk=576
+size.db.mem=920
+size.xfrd.mem=1160464
+size.config.disk=1
+size.config.mem=1064
+num.type.A=1
+num.type.NS=1
+num.type.MD=1
+num.type.MF=1
+num.type.CNAME=1
+num.type.SOA=1
+num.type.MB=1
+num.type.MG=1
+num.type.MR=1
+num.type.NULL=1
+num.type.WKS=1
+num.type.PTR=1
+num.type.HINFO=1
+num.type.MINFO=1
+num.type.MX=1
+num.type.TXT=1
+num.type.RP=1
+num.type.AFSDB=1
+num.type.X25=1
+num.type.ISDN=1
+num.type.RT=1
+num.type.NSAP=1
+num.type.SIG=1
+num.type.KEY=1
+num.type.PX=1
+num.type.AAAA=1
+num.type.LOC=1
+num.type.NXT=1
+num.type.SRV=1
+num.type.NAPTR=1
+num.type.KX=1
+num.type.CERT=1
+num.type.DNAME=1
+num.type.OPT=1
+num.type.APL=1
+num.type.DS=1
+num.type.SSHFP=1
+num.type.IPSECKEY=1
+num.type.RRSIG=1
+num.type.NSEC=1
+num.type.DNSKEY=1
+num.type.DHCID=1
+num.type.NSEC3=1
+num.type.NSEC3PARAM=1
+num.type.TLSA=1
+num.type.SMIMEA=1
+num.type.CDS=1
+num.type.CDNSKEY=1
+num.type.OPENPGPKEY=1
+num.type.CSYNC=1
+num.type.ZONEMD=1
+num.type.SVCB=1
+num.type.HTTPS=1
+num.type.SPF=1
+num.type.NID=1
+num.type.L32=1
+num.type.L64=1
+num.type.LP=1
+num.type.EUI48=1
+num.type.EUI64=1
+num.opcode.QUERY=1
+num.class.IN=1
+num.rcode.NOERROR=1
+num.rcode.FORMERR=1
+num.rcode.SERVFAIL=1
+num.rcode.NXDOMAIN=1
+num.rcode.NOTIMP=1
+num.rcode.REFUSED=1
+num.rcode.YXDOMAIN=1
+num.edns=1
+num.ednserr=1
+num.udp=1
+num.udp6=1
+num.tcp=1
+num.tcp6=1
+num.tls=1
+num.tls6=1
+num.answer_wo_aa=1
+num.rxerr=1
+num.txerr=1
+num.raxfr=1
+num.rixfr=1
+num.truncated=1
+num.dropped=1
+zone.master=1
+zone.slave=1