diff options
Diffstat (limited to '')
11 files changed, 1082 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/README.md b/src/go/collectors/go.d.plugin/modules/dnsquery/README.md new file mode 120000 index 000000000..c5baa8254 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/README.md @@ -0,0 +1 @@ +integrations/dns_query.md
\ No newline at end of file diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/charts.go b/src/go/collectors/go.d.plugin/modules/dnsquery/charts.go new file mode 100644 index 000000000..b229d89eb --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/charts.go @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package dnsquery + +import ( + "fmt" + "strings" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" +) + +const ( + prioDNSQueryStatus = module.Priority + iota + prioDNSQueryTime +) + +var ( + dnsChartsTmpl = module.Charts{ + dnsQueryStatusChartTmpl.Copy(), + dnsQueryTimeChartTmpl.Copy(), + } + dnsQueryStatusChartTmpl = module.Chart{ + ID: "server_%s_record_%s_query_status", + Title: "DNS Query Status", + Units: "status", + Fam: "query status", + Ctx: "dns_query.query_status", + Priority: prioDNSQueryStatus, + Dims: module.Dims{ + {ID: "server_%s_record_%s_query_status_success", Name: "success"}, + {ID: "server_%s_record_%s_query_status_network_error", Name: "network_error"}, + {ID: "server_%s_record_%s_query_status_dns_error", Name: "dns_error"}, + }, + } + dnsQueryTimeChartTmpl = module.Chart{ + ID: "server_%s_record_%s_query_time", + Title: "DNS Query Time", + Units: "seconds", + Fam: "query time", + Ctx: "dns_query.query_time", + Priority: prioDNSQueryTime, + Dims: module.Dims{ + {ID: "server_%s_record_%s_query_time", Name: "query_time", Div: 1e9}, + }, + } +) + +func newDNSServerCharts(server, network, rtype string) *module.Charts { + charts := dnsChartsTmpl.Copy() + + for _, chart := range *charts { + chart.ID = fmt.Sprintf(chart.ID, strings.ReplaceAll(server, ".", "_"), rtype) + chart.Labels = []module.Label{ + {Key: "server", Value: server}, + {Key: "network", Value: network}, + {Key: "record_type", Value: rtype}, + } + for _, d := range chart.Dims { + d.ID = fmt.Sprintf(d.ID, server, rtype) + } + } + + return charts +} diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/collect.go b/src/go/collectors/go.d.plugin/modules/dnsquery/collect.go new file mode 100644 index 000000000..a98e37cad --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/collect.go @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package dnsquery + +import ( + "math/rand" + "net" + "strconv" + "sync" + "time" + + "github.com/miekg/dns" +) + +func (d *DNSQuery) collect() (map[string]int64, error) { + if d.dnsClient == nil { + d.dnsClient = d.newDNSClient(d.Network, d.Timeout.Duration()) + } + + mx := make(map[string]int64) + domain := randomDomain(d.Domains) + d.Debugf("current domain : %s", domain) + + var wg sync.WaitGroup + var mux sync.RWMutex + for _, srv := range d.Servers { + for rtypeName, rtype := range d.recordTypes { + wg.Add(1) + go func(srv, rtypeName string, rtype uint16, wg *sync.WaitGroup) { + defer wg.Done() + + msg := new(dns.Msg) + msg.SetQuestion(dns.Fqdn(domain), rtype) + address := net.JoinHostPort(srv, strconv.Itoa(d.Port)) + + resp, rtt, err := d.dnsClient.Exchange(msg, address) + + mux.Lock() + defer mux.Unlock() + + px := "server_" + srv + "_record_" + rtypeName + "_" + + mx[px+"query_status_success"] = 0 + mx[px+"query_status_network_error"] = 0 + mx[px+"query_status_dns_error"] = 0 + + if err != nil { + d.Debugf("error on querying %s after %s query for %s : %s", srv, rtypeName, domain, err) + mx[px+"query_status_network_error"] = 1 + return + } + + if resp != nil && resp.Rcode != dns.RcodeSuccess { + d.Debugf("invalid answer from %s after %s query for %s (rcode %d)", srv, rtypeName, domain, resp.Rcode) + mx[px+"query_status_dns_error"] = 1 + } else { + mx[px+"query_status_success"] = 1 + } + mx["server_"+srv+"_record_"+rtypeName+"_query_time"] = rtt.Nanoseconds() + + }(srv, rtypeName, rtype, &wg) + } + } + wg.Wait() + + return mx, nil +} + +func randomDomain(domains []string) string { + src := rand.NewSource(time.Now().UnixNano()) + r := rand.New(src) + return domains[r.Intn(len(domains))] +} diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/config_schema.json b/src/go/collectors/go.d.plugin/modules/dnsquery/config_schema.json new file mode 100644 index 000000000..cfa6f3a14 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/config_schema.json @@ -0,0 +1,133 @@ +{ + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DNS query collector configuration.", + "type": "object", + "properties": { + "update_every": { + "title": "Update every", + "description": "Data collection interval, measured in seconds.", + "type": "integer", + "minimum": 1, + "default": 5 + }, + "timeout": { + "title": "Timeout", + "description": "Timeout for DNS queries, in seconds.", + "type": "number", + "default": 2 + }, + "network": { + "title": "Protocol", + "description": "Network protocol for DNS queries.", + "type": "string", + "enum": [ + "udp", + "tcp", + "tcp-tls" + ], + "default": "udp" + }, + "port": { + "title": "Port", + "description": "Port number for DNS servers.", + "type": "integer", + "default": 53 + }, + "record_types": { + "title": "Record types", + "description": "Types of DNS records to query for each server.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "enum": [ + "A", + "AAAA", + "ANY", + "CNAME", + "MX", + "NS", + "PTR", + "SOA", + "SPF", + "SRV", + "TXT" + ], + "default": "A" + }, + "default": [ + "A" + ], + "uniqueItems": true + }, + "servers": { + "title": "Servers", + "description": "List of DNS servers to query.", + "type": [ + "array", + "null" + ], + "items": { + "title": "DNS server", + "description": "IP address or hostname of the DNS server.", + "type": "string" + }, + "default": [ + "8.8.8.8" + ], + "uniqueItems": true, + "minItems": 1 + }, + "domains": { + "title": "Domains", + "description": "List of domains or subdomains to query. A random domain will be selected from this list at each iteration.", + "type": [ + "array", + "null" + ], + "items": { + "title": "Domain", + "type": "string" + }, + "default": [ + "google.com", + "github.com" + ], + "uniqueItems": true, + "minItems": 1 + } + }, + "required": [ + "domains", + "servers", + "network" + ], + "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)." + }, + "network": { + "ui:widget": "radio", + "ui:options": { + "inline": true + } + }, + "servers": { + "ui:listFlavour": "list" + }, + "domains": { + "ui:listFlavour": "list" + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/dnsquery.go b/src/go/collectors/go.d.plugin/modules/dnsquery/dnsquery.go new file mode 100644 index 000000000..5a0df7adc --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/dnsquery.go @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package dnsquery + +import ( + _ "embed" + "time" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" + + "github.com/miekg/dns" +) + +//go:embed "config_schema.json" +var configSchema string + +func init() { + module.Register("dns_query", module.Creator{ + JobConfigSchema: configSchema, + Defaults: module.Defaults{ + UpdateEvery: 5, + }, + Create: func() module.Module { return New() }, + Config: func() any { return &Config{} }, + }) +} + +func New() *DNSQuery { + return &DNSQuery{ + Config: Config{ + Timeout: web.Duration(time.Second * 2), + Network: "udp", + RecordTypes: []string{"A"}, + Port: 53, + }, + newDNSClient: func(network string, timeout time.Duration) dnsClient { + return &dns.Client{ + Net: network, + ReadTimeout: timeout, + } + }, + } +} + +type Config struct { + UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"` + Timeout web.Duration `yaml:"timeout,omitempty" json:"timeout"` + Domains []string `yaml:"domains" json:"domains"` + Servers []string `yaml:"servers" json:"servers"` + Network string `yaml:"network,omitempty" json:"network"` + RecordType string `yaml:"record_type,omitempty" json:"record_type"` + RecordTypes []string `yaml:"record_types,omitempty" json:"record_types"` + Port int `yaml:"port,omitempty" json:"port"` +} + +type ( + DNSQuery struct { + module.Base + Config `yaml:",inline" json:""` + + charts *module.Charts + + dnsClient dnsClient + newDNSClient func(network string, duration time.Duration) dnsClient + + recordTypes map[string]uint16 + } + dnsClient interface { + Exchange(msg *dns.Msg, address string) (response *dns.Msg, rtt time.Duration, err error) + } +) + +func (d *DNSQuery) Configuration() any { + return d.Config +} + +func (d *DNSQuery) Init() error { + if err := d.verifyConfig(); err != nil { + d.Errorf("config validation: %v", err) + return err + } + + rt, err := d.initRecordTypes() + if err != nil { + d.Errorf("init record type: %v", err) + return err + } + d.recordTypes = rt + + charts, err := d.initCharts() + if err != nil { + d.Errorf("init charts: %v", err) + return err + } + d.charts = charts + + return nil +} + +func (d *DNSQuery) Check() error { + return nil +} + +func (d *DNSQuery) Charts() *module.Charts { + return d.charts +} + +func (d *DNSQuery) Collect() map[string]int64 { + mx, err := d.collect() + if err != nil { + d.Error(err) + } + + if len(mx) == 0 { + return nil + } + return mx +} + +func (d *DNSQuery) Cleanup() {} diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/dnsquery_test.go b/src/go/collectors/go.d.plugin/modules/dnsquery/dnsquery_test.go new file mode 100644 index 000000000..9842e54fd --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/dnsquery_test.go @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package dnsquery + +import ( + "errors" + "os" + "testing" + "time" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" + + "github.com/miekg/dns" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + dataConfigJSON, _ = os.ReadFile("testdata/config.json") + dataConfigYAML, _ = os.ReadFile("testdata/config.yaml") +) + +func Test_testDataIsValid(t *testing.T) { + for name, data := range map[string][]byte{ + "dataConfigJSON": dataConfigJSON, + "dataConfigYAML": dataConfigYAML, + } { + require.NotNil(t, data, name) + } +} + +func TestDNSQuery_ConfigurationSerialize(t *testing.T) { + module.TestConfigurationSerialize(t, &DNSQuery{}, dataConfigJSON, dataConfigYAML) +} + +func TestDNSQuery_Init(t *testing.T) { + tests := map[string]struct { + wantFail bool + config Config + }{ + "success when all set": { + wantFail: false, + config: Config{ + Domains: []string{"example.com"}, + Servers: []string{"192.0.2.0"}, + Network: "udp", + RecordTypes: []string{"A"}, + Port: 53, + Timeout: web.Duration(time.Second), + }, + }, + "success when using deprecated record_type": { + wantFail: false, + config: Config{ + Domains: []string{"example.com"}, + Servers: []string{"192.0.2.0"}, + Network: "udp", + RecordType: "A", + Port: 53, + Timeout: web.Duration(time.Second), + }, + }, + "fail with default": { + wantFail: true, + config: New().Config, + }, + "fail when domains not set": { + wantFail: true, + config: Config{ + Domains: nil, + Servers: []string{"192.0.2.0"}, + Network: "udp", + RecordTypes: []string{"A"}, + Port: 53, + Timeout: web.Duration(time.Second), + }, + }, + "fail when servers not set": { + wantFail: true, + config: Config{ + Domains: []string{"example.com"}, + Servers: nil, + Network: "udp", + RecordTypes: []string{"A"}, + Port: 53, + Timeout: web.Duration(time.Second), + }, + }, + "fail when network is invalid": { + wantFail: true, + config: Config{ + Domains: []string{"example.com"}, + Servers: []string{"192.0.2.0"}, + Network: "gcp", + RecordTypes: []string{"A"}, + Port: 53, + Timeout: web.Duration(time.Second), + }, + }, + "fail when record_type is invalid": { + wantFail: true, + config: Config{ + Domains: []string{"example.com"}, + Servers: []string{"192.0.2.0"}, + Network: "udp", + RecordTypes: []string{"B"}, + Port: 53, + Timeout: web.Duration(time.Second), + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + dq := New() + dq.Config = test.config + + if test.wantFail { + assert.Error(t, dq.Init()) + } else { + assert.NoError(t, dq.Init()) + } + }) + } +} + +func TestDNSQuery_Check(t *testing.T) { + tests := map[string]struct { + wantFail bool + prepare func() *DNSQuery + }{ + "success when DNS query successful": { + wantFail: false, + prepare: caseDNSClientOK, + }, + "success when DNS query returns an error": { + wantFail: false, + prepare: caseDNSClientErr, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + dq := test.prepare() + + require.NoError(t, dq.Init()) + + if test.wantFail { + assert.Error(t, dq.Check()) + } else { + assert.NoError(t, dq.Check()) + } + }) + } +} + +func TestDNSQuery_Charts(t *testing.T) { + dq := New() + + dq.Domains = []string{"google.com"} + dq.Servers = []string{"192.0.2.0", "192.0.2.1"} + require.NoError(t, dq.Init()) + + assert.NotNil(t, dq.Charts()) + assert.Len(t, *dq.Charts(), len(dnsChartsTmpl)*len(dq.Servers)) +} + +func TestDNSQuery_Collect(t *testing.T) { + tests := map[string]struct { + prepare func() *DNSQuery + wantMetrics map[string]int64 + }{ + "success when DNS query successful": { + prepare: caseDNSClientOK, + wantMetrics: map[string]int64{ + "server_192.0.2.0_record_A_query_status_dns_error": 0, + "server_192.0.2.0_record_A_query_status_network_error": 0, + "server_192.0.2.0_record_A_query_status_success": 1, + "server_192.0.2.0_record_A_query_time": 1000000000, + "server_192.0.2.1_record_A_query_status_dns_error": 0, + "server_192.0.2.1_record_A_query_status_network_error": 0, + "server_192.0.2.1_record_A_query_status_success": 1, + "server_192.0.2.1_record_A_query_time": 1000000000, + }, + }, + "fail when DNS query returns an error": { + prepare: caseDNSClientErr, + wantMetrics: map[string]int64{ + "server_192.0.2.0_record_A_query_status_dns_error": 0, + "server_192.0.2.0_record_A_query_status_network_error": 1, + "server_192.0.2.0_record_A_query_status_success": 0, + "server_192.0.2.1_record_A_query_status_dns_error": 0, + "server_192.0.2.1_record_A_query_status_network_error": 1, + "server_192.0.2.1_record_A_query_status_success": 0, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + dq := test.prepare() + + require.NoError(t, dq.Init()) + + mx := dq.Collect() + + require.Equal(t, test.wantMetrics, mx) + }) + } +} + +func caseDNSClientOK() *DNSQuery { + dq := New() + dq.Domains = []string{"example.com"} + dq.Servers = []string{"192.0.2.0", "192.0.2.1"} + dq.newDNSClient = func(_ string, _ time.Duration) dnsClient { + return mockDNSClient{errOnExchange: false} + } + return dq +} + +func caseDNSClientErr() *DNSQuery { + dq := New() + dq.Domains = []string{"example.com"} + dq.Servers = []string{"192.0.2.0", "192.0.2.1"} + dq.newDNSClient = func(_ string, _ time.Duration) dnsClient { + return mockDNSClient{errOnExchange: true} + } + return dq +} + +type mockDNSClient struct { + errOnExchange bool +} + +func (m mockDNSClient) Exchange(_ *dns.Msg, _ string) (response *dns.Msg, rtt time.Duration, err error) { + if m.errOnExchange { + return nil, time.Second, errors.New("mock.Exchange() error") + } + return nil, time.Second, nil +} diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/init.go b/src/go/collectors/go.d.plugin/modules/dnsquery/init.go new file mode 100644 index 000000000..65af0ea2e --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/init.go @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package dnsquery + +import ( + "errors" + "fmt" + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + + "github.com/miekg/dns" +) + +func (d *DNSQuery) verifyConfig() error { + if len(d.Domains) == 0 { + return errors.New("no domains specified") + } + + if len(d.Servers) == 0 { + return errors.New("no servers specified") + } + + if !(d.Network == "" || d.Network == "udp" || d.Network == "tcp" || d.Network == "tcp-tls") { + return fmt.Errorf("wrong network transport : %s", d.Network) + } + + if d.RecordType != "" { + d.Warning("'record_type' config option is deprecated, use 'record_types' instead") + d.RecordTypes = append(d.RecordTypes, d.RecordType) + } + + if len(d.RecordTypes) == 0 { + return errors.New("no record types specified") + } + + return nil +} + +func (d *DNSQuery) initRecordTypes() (map[string]uint16, error) { + types := make(map[string]uint16) + for _, v := range d.RecordTypes { + rtype, err := parseRecordType(v) + if err != nil { + return nil, err + } + types[v] = rtype + + } + + return types, nil +} + +func (d *DNSQuery) initCharts() (*module.Charts, error) { + charts := module.Charts{} + + for _, srv := range d.Servers { + for _, rtype := range d.RecordTypes { + cs := newDNSServerCharts(srv, d.Network, rtype) + if err := charts.Add(*cs...); err != nil { + return nil, err + } + } + } + + return &charts, nil +} + +func parseRecordType(recordType string) (uint16, error) { + var rtype uint16 + + switch recordType { + case "A": + rtype = dns.TypeA + case "AAAA": + rtype = dns.TypeAAAA + case "ANY": + rtype = dns.TypeANY + case "CNAME": + rtype = dns.TypeCNAME + case "MX": + rtype = dns.TypeMX + case "NS": + rtype = dns.TypeNS + case "PTR": + rtype = dns.TypePTR + case "SOA": + rtype = dns.TypeSOA + case "SPF": + rtype = dns.TypeSPF + case "SRV": + rtype = dns.TypeSRV + case "TXT": + rtype = dns.TypeTXT + default: + return 0, fmt.Errorf("unknown record type : %s", recordType) + } + + return rtype, nil +} diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/integrations/dns_query.md b/src/go/collectors/go.d.plugin/modules/dnsquery/integrations/dns_query.md new file mode 100644 index 000000000..fccac8b59 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/integrations/dns_query.md @@ -0,0 +1,181 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/dnsquery/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/dnsquery/metadata.yaml" +sidebar_label: "DNS query" +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--> + +# DNS query + + +<img src="https://netdata.cloud/img/network-wired.svg" width="150"/> + + +Plugin: go.d.plugin +Module: dns_query + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +This module monitors DNS query round-trip time (RTT). + + + + +This collector is supported on all platforms. + +This collector supports collecting metrics from multiple instances of this integration, including remote instances. + + +### 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 server + +These metrics refer to the DNS server. + +Labels: + +| Label | Description | +|:-----------|:----------------| +| server | DNS server address. | +| network | Network protocol name (tcp, udp, tcp-tls). | +| record_type | DNS record type (e.g. A, AAAA, CNAME). | + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| dns_query.query_status | success, network_error, dns_error | status | +| dns_query.query_time | query_time | seconds | + + + +## Alerts + + +The following alerts are available: + +| Alert name | On metric | Description | +|:------------|:----------|:------------| +| [ dns_query_query_status ](https://github.com/netdata/netdata/blob/master/src/health/health.d/dns_query.conf) | dns_query.query_status | DNS request type ${label:record_type} to server ${label:server} is unsuccessful | + + +## Setup + +### Prerequisites + +No action required. + +### Configuration + +#### File + +The configuration file name for this integration is `go.d/dns_query.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/dns_query.conf +``` +#### Options + +The following options can be defined globally: update_every, autodetection_retry. + + +<details open><summary>All options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| update_every | Data collection frequency. | 1 | no | +| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no | +| domains | Domain or subdomains to query. The collector will choose a random domain from the list on every iteration. | | yes | +| servers | Servers to query. | | yes | +| port | DNS server port. | 53 | no | +| network | Network protocol name. Available options: udp, tcp, tcp-tls. | udp | no | +| record_types | Query record type. Available options: A, AAAA, CNAME, MX, NS, PTR, TXT, SOA, SPF, TXT, SRV. | A | no | +| timeout | Query read timeout. | 2 | no | + +</details> + +#### Examples + +##### Basic + +An example configuration. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: job1 + record_types: + - A + - AAAA + domains: + - google.com + - github.com + - reddit.com + servers: + - 8.8.8.8 + - 8.8.4.4 + +``` +</details> + + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `dns_query` 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 dns_query + ``` + + diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/metadata.yaml b/src/go/collectors/go.d.plugin/modules/dnsquery/metadata.yaml new file mode 100644 index 000000000..8c199550f --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/metadata.yaml @@ -0,0 +1,142 @@ +plugin_name: go.d.plugin +modules: + - meta: + id: collector-go.d.plugin-dns_query + plugin_name: go.d.plugin + module_name: dns_query + monitored_instance: + name: DNS query + link: "" + icon_filename: network-wired.svg + categories: + - data-collection.dns-and-dhcp-servers + keywords: + - dns + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + most_popular: false + overview: + data_collection: + metrics_description: | + This module monitors DNS query round-trip time (RTT). + method_description: "" + supported_platforms: + include: [] + exclude: [] + multi_instance: true + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: "" + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: [] + configuration: + file: + name: go.d/dns_query.conf + options: + description: | + The following options can be defined globally: update_every, autodetection_retry. + folding: + title: All options + enabled: true + list: + - name: update_every + description: Data collection frequency. + default_value: 1 + required: false + - name: autodetection_retry + description: Recheck interval in seconds. Zero means no recheck will be scheduled. + default_value: 0 + required: false + - name: domains + description: Domain or subdomains to query. The collector will choose a random domain from the list on every iteration. + default_value: "" + required: true + - name: servers + description: Servers to query. + default_value: "" + required: true + - name: port + description: DNS server port. + default_value: 53 + required: false + - name: network + description: "Network protocol name. Available options: udp, tcp, tcp-tls." + default_value: udp + required: false + - name: record_types + description: "Query record type. Available options: A, AAAA, CNAME, MX, NS, PTR, TXT, SOA, SPF, TXT, SRV." + default_value: A + required: false + - name: timeout + description: Query read timeout. + default_value: 2 + required: false + examples: + folding: + title: Config + enabled: true + list: + - name: Basic + description: An example configuration. + config: | + jobs: + - name: job1 + record_types: + - A + - AAAA + domains: + - google.com + - github.com + - reddit.com + servers: + - 8.8.8.8 + - 8.8.4.4 + troubleshooting: + problems: + list: [] + alerts: + - name: dns_query_query_status + metric: dns_query.query_status + info: "DNS request type ${label:record_type} to server ${label:server} is unsuccessful" + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/dns_query.conf + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: server + description: These metrics refer to the DNS server. + labels: + - name: server + description: DNS server address. + - name: network + description: Network protocol name (tcp, udp, tcp-tls). + - name: record_type + description: DNS record type (e.g. A, AAAA, CNAME). + metrics: + - name: dns_query.query_status + description: DNS Query Status + unit: status + chart_type: line + dimensions: + - name: success + - name: network_error + - name: dns_error + - name: dns_query.query_time + description: DNS Query Time + unit: seconds + chart_type: line + dimensions: + - name: query_time diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/testdata/config.json b/src/go/collectors/go.d.plugin/modules/dnsquery/testdata/config.json new file mode 100644 index 000000000..b16ed18c6 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/testdata/config.json @@ -0,0 +1,16 @@ +{ + "update_every": 123, + "domains": [ + "ok" + ], + "servers": [ + "ok" + ], + "network": "ok", + "record_type": "ok", + "record_types": [ + "ok" + ], + "port": 123, + "timeout": 123.123 +} diff --git a/src/go/collectors/go.d.plugin/modules/dnsquery/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/dnsquery/testdata/config.yaml new file mode 100644 index 000000000..6c6b014b6 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/dnsquery/testdata/config.yaml @@ -0,0 +1,11 @@ +update_every: 123 +domains: + - "ok" +servers: + - "ok" +network: "ok" +record_type: "ok" +record_types: + - "ok" +port: 123 +timeout: 123.123 |