diff options
Diffstat (limited to '')
14 files changed, 2512 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/README.md b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/README.md new file mode 120000 index 000000000..810e63308 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/README.md @@ -0,0 +1 @@ +integrations/powerdns_recursor.md
\ No newline at end of file diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/charts.go b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/charts.go new file mode 100644 index 000000000..d0bd7c36e --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/charts.go @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package powerdns_recursor + +import "github.com/netdata/netdata/go/go.d.plugin/agent/module" + +var charts = module.Charts{ + { + ID: "questions_in", + Title: "Incoming questions", + Units: "questions/s", + Fam: "questions", + Ctx: "powerdns_recursor.questions_in", + Dims: module.Dims{ + {ID: "questions", Name: "total", Algo: module.Incremental}, + {ID: "tcp-questions", Name: "tcp", Algo: module.Incremental}, + {ID: "ipv6-questions", Name: "ipv6", Algo: module.Incremental}, + }, + }, + { + ID: "questions_out", + Title: "Outgoing questions", + Units: "questions/s", + Fam: "questions", + Ctx: "powerdns_recursor.questions_out", + Dims: module.Dims{ + {ID: "all-outqueries", Name: "udp", Algo: module.Incremental}, + {ID: "tcp-outqueries", Name: "tcp", Algo: module.Incremental}, + {ID: "ipv6-outqueries", Name: "ipv6", Algo: module.Incremental}, + {ID: "throttled-outqueries", Name: "throttled", Algo: module.Incremental}, + }, + }, + { + ID: "answer_time", + Title: "Queries answered within a time range", + Units: "queries/s", + Fam: "performance", + Ctx: "powerdns_recursor.answer_time", + Dims: module.Dims{ + {ID: "answers0-1", Name: "0-1ms", Algo: module.Incremental}, + {ID: "answers1-10", Name: "1-10ms", Algo: module.Incremental}, + {ID: "answers10-100", Name: "10-100ms", Algo: module.Incremental}, + {ID: "answers100-1000", Name: "100-1000ms", Algo: module.Incremental}, + {ID: "answers-slow", Name: "slow", Algo: module.Incremental}, + }, + }, + { + ID: "timeouts", + Title: "Timeouts on outgoing UDP queries", + Units: "timeouts/s", + Fam: "performance", + Ctx: "powerdns_recursor.timeouts", + Dims: module.Dims{ + {ID: "outgoing-timeouts", Name: "total", Algo: module.Incremental}, + {ID: "outgoing4-timeouts", Name: "ipv4", Algo: module.Incremental}, + {ID: "outgoing6-timeouts", Name: "ipv6", Algo: module.Incremental}, + }, + }, + { + ID: "drops", + Title: "Drops", + Units: "drops/s", + Fam: "performance", + Ctx: "powerdns_recursor.drops", + Dims: module.Dims{ + {ID: "over-capacity-drops", Algo: module.Incremental}, + {ID: "query-pipe-full-drops", Algo: module.Incremental}, + {ID: "too-old-drops", Algo: module.Incremental}, + {ID: "truncated-drops", Algo: module.Incremental}, + {ID: "empty-queries", Algo: module.Incremental}, + }, + }, + { + ID: "cache_usage", + Title: "Cache Usage", + Units: "events/s", + Fam: "cache", + Ctx: "powerdns_recursor.cache_usage", + Dims: module.Dims{ + {ID: "cache-hits", Algo: module.Incremental}, + {ID: "cache-misses", Algo: module.Incremental}, + {ID: "packetcache-hits", Name: "packet-cache-hits", Algo: module.Incremental}, + {ID: "packetcache-misses", Name: "packet-cache-misses", Algo: module.Incremental}, + }, + }, + { + ID: "cache_size", + Title: "Cache Size", + Units: "entries", + Fam: "cache", + Ctx: "powerdns_recursor.cache_size", + Dims: module.Dims{ + {ID: "cache-entries", Name: "cache"}, + {ID: "packetcache-entries", Name: "packet-cache"}, + {ID: "negcache-entries", Name: "negative-cache"}, + }, + }, +} diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/collect.go b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/collect.go new file mode 100644 index 000000000..ec2e99c90 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/collect.go @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package powerdns_recursor + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strconv" + + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" +) + +const ( + urlPathLocalStatistics = "/api/v1/servers/localhost/statistics" +) + +func (r *Recursor) collect() (map[string]int64, error) { + statistics, err := r.scrapeStatistics() + if err != nil { + return nil, err + } + + collected := make(map[string]int64) + + r.collectStatistics(collected, statistics) + + if !isPowerDNSRecursorMetrics(collected) { + return nil, errors.New("returned metrics aren't PowerDNS Recursor metrics") + } + + return collected, nil +} + +func isPowerDNSRecursorMetrics(collected map[string]int64) bool { + // PowerDNS Authoritative Server has same endpoint and returns data in the same format. + _, ok1 := collected["over-capacity-drops"] + _, ok2 := collected["tcp-questions"] + return ok1 && ok2 +} + +func (r *Recursor) collectStatistics(collected map[string]int64, statistics statisticMetrics) { + for _, s := range statistics { + // https://doc.powerdns.com/authoritative/http-api/statistics.html#statisticitem + if s.Type != "StatisticItem" { + continue + } + + value, ok := s.Value.(string) + if !ok { + r.Debugf("%s value (%v) unexpected type: want=string, got=%T.", s.Name, s.Value, s.Value) + continue + } + + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + r.Debugf("%s value (%v) parse error: %v", s.Name, s.Value, err) + continue + } + + collected[s.Name] = v + } +} + +func (r *Recursor) scrapeStatistics() ([]statisticMetric, error) { + req, _ := web.NewHTTPRequest(r.Request) + req.URL.Path = urlPathLocalStatistics + + var statistics statisticMetrics + if err := r.doOKDecode(req, &statistics); err != nil { + return nil, err + } + + return statistics, nil +} + +func (r *Recursor) doOKDecode(req *http.Request, in interface{}) error { + resp, err := r.httpClient.Do(req) + if err != nil { + return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err) + } + defer closeBody(resp) + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode) + } + + if err := json.NewDecoder(resp.Body).Decode(in); err != nil { + return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err) + } + return nil +} + +func closeBody(resp *http.Response) { + if resp != nil && resp.Body != nil { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + } +} diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/config_schema.json b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/config_schema.json new file mode 100644 index 000000000..f175548ba --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/config_schema.json @@ -0,0 +1,177 @@ +{ + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PowerDNS Recursor collector configuration.", + "type": "object", + "properties": { + "update_every": { + "title": "Update every", + "description": "Data collection interval, measured in seconds.", + "type": "integer", + "minimum": 1, + "default": 1 + }, + "url": { + "title": "URL", + "description": "The URL of the PowerDNS Recursor [built-in webserver](https://doc.powerdns.com/recursor/http-api/index.html#webserver).", + "type": "string", + "default": "http://127.0.0.1:8081", + "format": "uri" + }, + "timeout": { + "title": "Timeout", + "description": "The timeout in seconds for the HTTP request.", + "type": "number", + "minimum": 0.5, + "default": 1 + }, + "not_follow_redirects": { + "title": "Not follow redirects", + "description": "If set, the client will not follow HTTP redirects automatically.", + "type": "boolean" + }, + "username": { + "title": "Username", + "description": "The username for basic authentication.", + "type": "string", + "sensitive": true + }, + "password": { + "title": "Password", + "description": "The password for basic authentication.", + "type": "string", + "sensitive": true + }, + "proxy_url": { + "title": "Proxy URL", + "description": "The URL of the proxy server.", + "type": "string" + }, + "proxy_username": { + "title": "Proxy username", + "description": "The username for proxy authentication.", + "type": "string", + "sensitive": true + }, + "proxy_password": { + "title": "Proxy password", + "description": "The password for proxy authentication.", + "type": "string", + "sensitive": true + }, + "headers": { + "title": "Headers", + "description": "Additional HTTP headers to include in the request.", + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "string" + } + }, + "tls_skip_verify": { + "title": "Skip TLS verification", + "description": "If set, TLS certificate verification will be skipped.", + "type": "boolean" + }, + "tls_ca": { + "title": "TLS CA", + "description": "The path to the CA certificate file for TLS verification.", + "type": "string", + "pattern": "^$|^/" + }, + "tls_cert": { + "title": "TLS certificate", + "description": "The path to the client certificate file for TLS authentication.", + "type": "string", + "pattern": "^$|^/" + }, + "tls_key": { + "title": "TLS key", + "description": "The path to the client key file for TLS authentication.", + "type": "string", + "pattern": "^$|^/" + }, + "body": { + "title": "Body", + "type": "string" + }, + "method": { + "title": "Method", + "type": "string" + } + }, + "required": [ + "url" + ], + "additionalProperties": false, + "patternProperties": { + "^name$": {} + } + }, + "uiSchema": { + "ui:flavour": "tabs", + "ui:options": { + "tabs": [ + { + "title": "Base", + "fields": [ + "update_every", + "url", + "timeout", + "not_follow_redirects" + ] + }, + { + "title": "Auth", + "fields": [ + "username", + "password" + ] + }, + { + "title": "TLS", + "fields": [ + "tls_skip_verify", + "tls_ca", + "tls_cert", + "tls_key" + ] + }, + { + "title": "Proxy", + "fields": [ + "proxy_url", + "proxy_username", + "proxy_password" + ] + }, + { + "title": "Headers", + "fields": [ + "headers" + ] + } + ] + }, + "uiOptions": { + "fullPage": true + }, + "body": { + "ui:widget": "hidden" + }, + "method": { + "ui:widget": "hidden" + }, + "timeout": { + "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)." + }, + "password": { + "ui:widget": "password" + }, + "proxy_password": { + "ui:widget": "password" + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/init.go b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/init.go new file mode 100644 index 000000000..d242950f5 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/init.go @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package powerdns_recursor + +import ( + "errors" + "net/http" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" +) + +func (r *Recursor) validateConfig() error { + if r.URL == "" { + return errors.New("URL not set") + } + if _, err := web.NewHTTPRequest(r.Request); err != nil { + return err + } + return nil +} + +func (r *Recursor) initHTTPClient() (*http.Client, error) { + return web.NewHTTPClient(r.Client) +} + +func (r *Recursor) initCharts() (*module.Charts, error) { + return charts.Copy(), nil +} diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/integrations/powerdns_recursor.md b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/integrations/powerdns_recursor.md new file mode 100644 index 000000000..4821aac5d --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/integrations/powerdns_recursor.md @@ -0,0 +1,226 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/powerdns_recursor/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/powerdns_recursor/metadata.yaml" +sidebar_label: "PowerDNS Recursor" +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--> + +# PowerDNS Recursor + + +<img src="https://netdata.cloud/img/powerdns.svg" width="150"/> + + +Plugin: go.d.plugin +Module: powerdns_recursor + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +This collector monitors PowerDNS Recursor instances. + +It collects metrics from [the internal webserver](https://doc.powerdns.com/recursor/http-api/index.html#built-in-webserver-and-http-api). + +Used endpoints: + +- [`/api/v1/servers/localhost/statistics`](https://doc.powerdns.com/recursor/common/api/endpoint-statistics.html) + + + + +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 PowerDNS Recursor instance + +These metrics refer to the entire monitored application. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| powerdns_recursor.questions_in | total, tcp, ipv6 | questions/s | +| powerdns_recursor.questions_out | udp, tcp, ipv6, throttled | questions/s | +| powerdns_recursor.answer_time | 0-1ms, 1-10ms, 10-100ms, 100-1000ms, slow | queries/s | +| powerdns_recursor.timeouts | total, ipv4, ipv6 | timeouts/s | +| powerdns_recursor.drops | over-capacity-drops, query-pipe-full-drops, too-old-drops, truncated-drops, empty-queries | drops/s | +| powerdns_recursor.cache_usage | cache-hits, cache-misses, packet-cache-hits, packet-cache-misses | events/s | +| powerdns_recursor.cache_size | cache, packet-cache, negative-cache | entries | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +#### Enable webserver + +Follow [webserver](https://doc.powerdns.com/recursor/http-api/index.html#webserver) documentation. + + +#### Enable HTTP API + +Follow [HTTP API](https://doc.powerdns.com/recursor/http-api/index.html#enabling-the-api) documentation. + + + +### Configuration + +#### File + +The configuration file name for this integration is `go.d/powerdns_recursor.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/powerdns_recursor.conf +``` +#### Options + +The following options can be defined globally: update_every, autodetection_retry. + + +<details open><summary>Config options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| update_every | Data collection frequency. | 5 | no | +| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no | +| url | Server URL. | http://127.0.0.1:8081 | yes | +| timeout | HTTP request timeout. | 1 | no | +| username | Username for basic HTTP authentication. | | no | +| password | Password for basic HTTP authentication. | | no | +| proxy_url | Proxy URL. | | no | +| proxy_username | Username for proxy basic HTTP authentication. | | no | +| proxy_password | Password for proxy basic HTTP authentication. | | no | +| method | HTTP request method. | GET | no | +| body | HTTP request body. | | no | +| headers | HTTP request headers. | | no | +| not_follow_redirects | Redirect handling policy. Controls whether the client follows redirects. | no | no | +| tls_skip_verify | Server certificate chain and hostname validation policy. Controls whether the client performs this check. | no | no | +| tls_ca | Certification authority that the client uses when verifying the server's certificates. | | no | +| tls_cert | Client TLS certificate. | | no | +| tls_key | Client TLS key. | | no | + +</details> + +#### Examples + +##### Basic + +An example configuration. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: http://127.0.0.1:8081 + +``` +</details> + +##### HTTP authentication + +Basic HTTP authentication. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: http://127.0.0.1:8081 + username: admin + password: password + +``` +</details> + +##### Multi-instance + +> **Note**: When you define multiple jobs, their names must be unique. + +Local and remote instances. + + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: http://127.0.0.1:8081 + + - name: remote + url: http://203.0.113.0:8081 + +``` +</details> + + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `powerdns_recursor` 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 powerdns_recursor + ``` + + diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/metadata.yaml b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/metadata.yaml new file mode 100644 index 000000000..82cb99127 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/metadata.yaml @@ -0,0 +1,240 @@ +plugin_name: go.d.plugin +modules: + - meta: + id: collector-go.d.plugin-powerdns_recursor + plugin_name: go.d.plugin + module_name: powerdns_recursor + monitored_instance: + name: PowerDNS Recursor + link: https://doc.powerdns.com/recursor/ + icon_filename: powerdns.svg + categories: + - data-collection.dns-and-dhcp-servers + keywords: + - powerdns + - dns + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + most_popular: false + overview: + data_collection: + metrics_description: | + This collector monitors PowerDNS Recursor instances. + + It collects metrics from [the internal webserver](https://doc.powerdns.com/recursor/http-api/index.html#built-in-webserver-and-http-api). + + Used endpoints: + + - [`/api/v1/servers/localhost/statistics`](https://doc.powerdns.com/recursor/common/api/endpoint-statistics.html) + 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: + - title: Enable webserver + description: | + Follow [webserver](https://doc.powerdns.com/recursor/http-api/index.html#webserver) documentation. + - title: Enable HTTP API + description: | + Follow [HTTP API](https://doc.powerdns.com/recursor/http-api/index.html#enabling-the-api) documentation. + configuration: + file: + name: go.d/powerdns_recursor.conf + options: + description: | + The following options can be defined globally: update_every, autodetection_retry. + folding: + title: Config options + enabled: true + list: + - name: update_every + description: Data collection frequency. + default_value: 5 + required: false + - name: autodetection_retry + description: Recheck interval in seconds. Zero means no recheck will be scheduled. + default_value: 0 + required: false + - name: url + description: Server URL. + default_value: http://127.0.0.1:8081 + required: true + - name: timeout + description: HTTP request timeout. + default_value: 1 + required: false + - name: username + description: Username for basic HTTP authentication. + default_value: "" + required: false + - name: password + description: Password for basic HTTP authentication. + default_value: "" + required: false + - name: proxy_url + description: Proxy URL. + default_value: "" + required: false + - name: proxy_username + description: Username for proxy basic HTTP authentication. + default_value: "" + required: false + - name: proxy_password + description: Password for proxy basic HTTP authentication. + default_value: "" + required: false + - name: method + description: HTTP request method. + default_value: GET + required: false + - name: body + description: HTTP request body. + default_value: "" + required: false + - name: headers + description: HTTP request headers. + default_value: "" + required: false + - name: not_follow_redirects + description: Redirect handling policy. Controls whether the client follows redirects. + default_value: false + required: false + - name: tls_skip_verify + description: Server certificate chain and hostname validation policy. Controls whether the client performs this check. + default_value: false + required: false + - name: tls_ca + description: Certification authority that the client uses when verifying the server's certificates. + default_value: "" + required: false + - name: tls_cert + description: Client TLS certificate. + default_value: "" + required: false + - name: tls_key + description: Client TLS key. + default_value: "" + required: false + examples: + folding: + title: Config + enabled: true + list: + - name: Basic + description: An example configuration. + config: | + jobs: + - name: local + url: http://127.0.0.1:8081 + - name: HTTP authentication + description: Basic HTTP authentication. + config: | + jobs: + - name: local + url: http://127.0.0.1:8081 + username: admin + password: password + - name: Multi-instance + description: | + > **Note**: When you define multiple jobs, their names must be unique. + + Local and remote instances. + config: | + jobs: + - name: local + url: http://127.0.0.1:8081 + + - name: remote + url: http://203.0.113.0:8081 + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: global + description: These metrics refer to the entire monitored application. + labels: [] + metrics: + - name: powerdns_recursor.questions_in + description: Incoming questions + unit: questions/s + chart_type: line + dimensions: + - name: total + - name: tcp + - name: ipv6 + - name: powerdns_recursor.questions_out + description: Outgoing questions + unit: questions/s + chart_type: line + dimensions: + - name: udp + - name: tcp + - name: ipv6 + - name: throttled + - name: powerdns_recursor.answer_time + description: Queries answered within a time range + unit: queries/s + chart_type: line + dimensions: + - name: 0-1ms + - name: 1-10ms + - name: 10-100ms + - name: 100-1000ms + - name: slow + - name: powerdns_recursor.timeouts + description: Timeouts on outgoing UDP queries + unit: timeouts/s + chart_type: line + dimensions: + - name: total + - name: ipv4 + - name: ipv6 + - name: powerdns_recursor.drops + description: Drops + unit: drops/s + chart_type: line + dimensions: + - name: over-capacity-drops + - name: query-pipe-full-drops + - name: too-old-drops + - name: truncated-drops + - name: empty-queries + - name: powerdns_recursor.cache_usage + description: Cache Usage + unit: events/s + chart_type: line + dimensions: + - name: cache-hits + - name: cache-misses + - name: packet-cache-hits + - name: packet-cache-misses + - name: powerdns_recursor.cache_size + description: Cache Size + unit: entries + chart_type: line + dimensions: + - name: cache + - name: packet-cache + - name: negative-cache diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/metrics.go b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/metrics.go new file mode 100644 index 000000000..a7fbd63c1 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/metrics.go @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package powerdns_recursor + +// https://doc.powerdns.com/recursor/metrics.html +// https://docs.powerdns.com/recursor/performance.html#recursor-caches + +// PowerDNS Recursor documentation has no section about statistics objects, +// fortunately authoritative has. +// https://doc.powerdns.com/authoritative/http-api/statistics.html#objects +type ( + statisticMetrics []statisticMetric + statisticMetric struct { + Name string + Type string + Value interface{} + } +) diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/recursor.go b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/recursor.go new file mode 100644 index 000000000..ec5d8da6d --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/recursor.go @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package powerdns_recursor + +import ( + _ "embed" + "errors" + "net/http" + "time" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" +) + +//go:embed "config_schema.json" +var configSchema string + +func init() { + module.Register("powerdns_recursor", module.Creator{ + JobConfigSchema: configSchema, + Create: func() module.Module { return New() }, + Config: func() any { return &Config{} }, + }) +} + +func New() *Recursor { + return &Recursor{ + Config: Config{ + HTTP: web.HTTP{ + Request: web.Request{ + URL: "http://127.0.0.1:8081", + }, + Client: web.Client{ + Timeout: web.Duration(time.Second), + }, + }, + }, + } +} + +type Config struct { + UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"` + web.HTTP `yaml:",inline" json:""` +} + +type Recursor struct { + module.Base + Config `yaml:",inline" json:""` + + charts *module.Charts + + httpClient *http.Client +} + +func (r *Recursor) Configuration() any { + return r.Config +} + +func (r *Recursor) Init() error { + err := r.validateConfig() + if err != nil { + r.Errorf("config validation: %v", err) + return err + } + + client, err := r.initHTTPClient() + if err != nil { + r.Errorf("init HTTP client: %v", err) + return err + } + r.httpClient = client + + cs, err := r.initCharts() + if err != nil { + r.Errorf("init charts: %v", err) + return err + } + r.charts = cs + + return nil +} + +func (r *Recursor) Check() error { + mx, err := r.collect() + if err != nil { + r.Error(err) + return err + } + if len(mx) == 0 { + return errors.New("no metrics collected") + } + return nil +} + +func (r *Recursor) Charts() *module.Charts { + return r.charts +} + +func (r *Recursor) Collect() map[string]int64 { + ms, err := r.collect() + if err != nil { + r.Error(err) + } + + if len(ms) == 0 { + return nil + } + return ms +} + +func (r *Recursor) Cleanup() { + if r.httpClient == nil { + return + } + r.httpClient.CloseIdleConnections() +} diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/recursor_test.go b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/recursor_test.go new file mode 100644 index 000000000..f4ab0535f --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/recursor_test.go @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package powerdns_recursor + +import ( + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/tlscfg" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + dataConfigJSON, _ = os.ReadFile("testdata/config.json") + dataConfigYAML, _ = os.ReadFile("testdata/config.yaml") + + dataVer431statistics, _ = os.ReadFile("testdata/v4.3.1/statistics.json") + dataAuthoritativeStatistics, _ = os.ReadFile("testdata/authoritative/statistics.json") +) + +func Test_testDataIsValid(t *testing.T) { + for name, data := range map[string][]byte{ + "dataConfigJSON": dataConfigJSON, + "dataConfigYAML": dataConfigYAML, + "dataVer431statistics": dataVer431statistics, + "dataAuthoritativeStatistics": dataAuthoritativeStatistics, + } { + require.NotNil(t, data, name) + } +} + +func TestRecursor_ConfigurationSerialize(t *testing.T) { + module.TestConfigurationSerialize(t, &Recursor{}, dataConfigJSON, dataConfigYAML) +} + +func TestRecursor_Init(t *testing.T) { + tests := map[string]struct { + config Config + wantFail bool + }{ + "success on default config": { + config: New().Config, + }, + "fails on unset URL": { + wantFail: true, + config: Config{ + HTTP: web.HTTP{ + Request: web.Request{URL: ""}, + }, + }, + }, + "fails on invalid TLSCA": { + wantFail: true, + config: Config{ + HTTP: web.HTTP{ + Request: web.Request{ + URL: "http://127.0.0.1:38001", + }, + Client: web.Client{ + TLSConfig: tlscfg.TLSConfig{TLSCA: "testdata/tls"}, + }, + }, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + recursor := New() + recursor.Config = test.config + + if test.wantFail { + assert.Error(t, recursor.Init()) + } else { + assert.NoError(t, recursor.Init()) + } + }) + } +} + +func TestRecursor_Check(t *testing.T) { + tests := map[string]struct { + prepare func() (r *Recursor, cleanup func()) + wantFail bool + }{ + "success on valid response v4.3.1": { + prepare: preparePowerDNSRecursorV431, + }, + "fails on response from PowerDNS Authoritative Server": { + wantFail: true, + prepare: preparePowerDNSRecursorAuthoritativeData, + }, + "fails on 404 response": { + wantFail: true, + prepare: preparePowerDNSRecursor404, + }, + "fails on connection refused": { + wantFail: true, + prepare: preparePowerDNSRecursorConnectionRefused, + }, + "fails on response with invalid data": { + wantFail: true, + prepare: preparePowerDNSRecursorInvalidData, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + recursor, cleanup := test.prepare() + defer cleanup() + require.NoError(t, recursor.Init()) + + if test.wantFail { + assert.Error(t, recursor.Check()) + } else { + assert.NoError(t, recursor.Check()) + } + }) + } +} + +func TestRecursor_Charts(t *testing.T) { + recursor := New() + require.NoError(t, recursor.Init()) + assert.NotNil(t, recursor.Charts()) +} + +func TestRecursor_Cleanup(t *testing.T) { + assert.NotPanics(t, New().Cleanup) +} + +func TestRecursor_Collect(t *testing.T) { + tests := map[string]struct { + prepare func() (r *Recursor, cleanup func()) + wantCollected map[string]int64 + }{ + "success on valid response v4.3.1": { + prepare: preparePowerDNSRecursorV431, + wantCollected: map[string]int64{ + "all-outqueries": 41, + "answers-slow": 1, + "answers0-1": 1, + "answers1-10": 1, + "answers10-100": 1, + "answers100-1000": 1, + "auth-zone-queries": 1, + "auth4-answers-slow": 1, + "auth4-answers0-1": 1, + "auth4-answers1-10": 5, + "auth4-answers10-100": 35, + "auth4-answers100-1000": 1, + "auth6-answers-slow": 1, + "auth6-answers0-1": 1, + "auth6-answers1-10": 1, + "auth6-answers10-100": 1, + "auth6-answers100-1000": 1, + "cache-entries": 171, + "cache-hits": 1, + "cache-misses": 1, + "case-mismatches": 1, + "chain-resends": 1, + "client-parse-errors": 1, + "concurrent-queries": 1, + "cpu-msec-thread-0": 439, + "cpu-msec-thread-1": 445, + "cpu-msec-thread-2": 466, + "dlg-only-drops": 1, + "dnssec-authentic-data-queries": 1, + "dnssec-check-disabled-queries": 1, + "dnssec-queries": 1, + "dnssec-result-bogus": 1, + "dnssec-result-indeterminate": 1, + "dnssec-result-insecure": 1, + "dnssec-result-nta": 1, + "dnssec-result-secure": 5, + "dnssec-validations": 5, + "dont-outqueries": 1, + "ecs-queries": 1, + "ecs-responses": 1, + "edns-ping-matches": 1, + "edns-ping-mismatches": 1, + "empty-queries": 1, + "failed-host-entries": 1, + "fd-usage": 32, + "ignored-packets": 1, + "ipv6-outqueries": 1, + "ipv6-questions": 1, + "malloc-bytes": 1, + "max-cache-entries": 1000000, + "max-mthread-stack": 1, + "max-packetcache-entries": 500000, + "negcache-entries": 1, + "no-packet-error": 1, + "noedns-outqueries": 1, + "noerror-answers": 1, + "noping-outqueries": 1, + "nsset-invalidations": 1, + "nsspeeds-entries": 78, + "nxdomain-answers": 1, + "outgoing-timeouts": 1, + "outgoing4-timeouts": 1, + "outgoing6-timeouts": 1, + "over-capacity-drops": 1, + "packetcache-entries": 1, + "packetcache-hits": 1, + "packetcache-misses": 1, + "policy-drops": 1, + "policy-result-custom": 1, + "policy-result-drop": 1, + "policy-result-noaction": 1, + "policy-result-nodata": 1, + "policy-result-nxdomain": 1, + "policy-result-truncate": 1, + "qa-latency": 1, + "qname-min-fallback-success": 1, + "query-pipe-full-drops": 1, + "questions": 1, + "real-memory-usage": 44773376, + "rebalanced-queries": 1, + "resource-limits": 1, + "security-status": 3, + "server-parse-errors": 1, + "servfail-answers": 1, + "spoof-prevents": 1, + "sys-msec": 1520, + "tcp-client-overflow": 1, + "tcp-clients": 1, + "tcp-outqueries": 1, + "tcp-questions": 1, + "throttle-entries": 1, + "throttled-out": 1, + "throttled-outqueries": 1, + "too-old-drops": 1, + "truncated-drops": 1, + "udp-in-errors": 1, + "udp-noport-errors": 1, + "udp-recvbuf-errors": 1, + "udp-sndbuf-errors": 1, + "unauthorized-tcp": 1, + "unauthorized-udp": 1, + "unexpected-packets": 1, + "unreachables": 1, + "uptime": 1624, + "user-msec": 465, + "variable-responses": 1, + "x-our-latency": 1, + "x-ourtime-slow": 1, + "x-ourtime0-1": 1, + "x-ourtime1-2": 1, + "x-ourtime16-32": 1, + "x-ourtime2-4": 1, + "x-ourtime4-8": 1, + "x-ourtime8-16": 1, + }, + }, + "fails on response from PowerDNS Authoritative Server": { + prepare: preparePowerDNSRecursorAuthoritativeData, + }, + "fails on 404 response": { + prepare: preparePowerDNSRecursor404, + }, + "fails on connection refused": { + prepare: preparePowerDNSRecursorConnectionRefused, + }, + "fails on response with invalid data": { + prepare: preparePowerDNSRecursorInvalidData, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + recursor, cleanup := test.prepare() + defer cleanup() + require.NoError(t, recursor.Init()) + + collected := recursor.Collect() + + assert.Equal(t, test.wantCollected, collected) + if len(test.wantCollected) > 0 { + ensureCollectedHasAllChartsDimsVarsIDs(t, recursor, collected) + } + }) + } +} + +func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, rec *Recursor, collected map[string]int64) { + for _, chart := range *rec.Charts() { + if chart.Obsolete { + continue + } + for _, dim := range chart.Dims { + _, ok := collected[dim.ID] + assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", dim.ID, chart.ID) + } + for _, v := range chart.Vars { + _, ok := collected[v.ID] + assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", v.ID, chart.ID) + } + } +} + +func preparePowerDNSRecursorV431() (*Recursor, func()) { + srv := preparePowerDNSRecursorEndpoint() + recursor := New() + recursor.URL = srv.URL + + return recursor, srv.Close +} + +func preparePowerDNSRecursorAuthoritativeData() (*Recursor, func()) { + srv := preparePowerDNSAuthoritativeEndpoint() + recursor := New() + recursor.URL = srv.URL + + return recursor, srv.Close +} + +func preparePowerDNSRecursorInvalidData() (*Recursor, func()) { + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte("hello and\n goodbye")) + })) + recursor := New() + recursor.URL = srv.URL + + return recursor, srv.Close +} + +func preparePowerDNSRecursor404() (*Recursor, func()) { + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + recursor := New() + recursor.URL = srv.URL + + return recursor, srv.Close +} + +func preparePowerDNSRecursorConnectionRefused() (*Recursor, func()) { + recursor := New() + recursor.URL = "http://127.0.0.1:38001" + + return recursor, func() {} +} + +func preparePowerDNSRecursorEndpoint() *httptest.Server { + return httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case urlPathLocalStatistics: + _, _ = w.Write(dataVer431statistics) + default: + w.WriteHeader(http.StatusNotFound) + } + })) +} + +func preparePowerDNSAuthoritativeEndpoint() *httptest.Server { + return httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case urlPathLocalStatistics: + _, _ = w.Write(dataAuthoritativeStatistics) + default: + w.WriteHeader(http.StatusNotFound) + } + })) +} diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/authoritative/statistics.json b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/authoritative/statistics.json new file mode 100644 index 000000000..72bb2f0a2 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/authoritative/statistics.json @@ -0,0 +1,507 @@ +[ + { + "name": "corrupt-packets", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "cpu-iowait", + "type": "StatisticItem", + "value": "513" + }, + { + "name": "cpu-steal", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "deferred-cache-inserts", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "deferred-cache-lookup", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "deferred-packetcache-inserts", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "deferred-packetcache-lookup", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "dnsupdate-answers", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "dnsupdate-changes", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "dnsupdate-queries", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "dnsupdate-refused", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "fd-usage", + "type": "StatisticItem", + "value": "23" + }, + { + "name": "incoming-notifications", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "key-cache-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "latency", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "meta-cache-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "open-tcp-connections", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "overload-drops", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "packetcache-hit", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "packetcache-miss", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "packetcache-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "qsize-q", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "query-cache-hit", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "query-cache-miss", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "query-cache-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "rd-queries", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "real-memory-usage", + "type": "StatisticItem", + "value": "164507648" + }, + { + "name": "recursing-answers", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "recursing-questions", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "recursion-unanswered", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "ring-logmessages-capacity", + "type": "StatisticItem", + "value": "10000" + }, + { + "name": "ring-logmessages-size", + "type": "StatisticItem", + "value": "10" + }, + { + "name": "ring-noerror-queries-capacity", + "type": "StatisticItem", + "value": "10000" + }, + { + "name": "ring-noerror-queries-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "ring-nxdomain-queries-capacity", + "type": "StatisticItem", + "value": "10000" + }, + { + "name": "ring-nxdomain-queries-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "ring-queries-capacity", + "type": "StatisticItem", + "value": "10000" + }, + { + "name": "ring-queries-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "ring-remotes-capacity", + "type": "StatisticItem", + "value": "10000" + }, + { + "name": "ring-remotes-corrupt-capacity", + "type": "StatisticItem", + "value": "10000" + }, + { + "name": "ring-remotes-corrupt-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "ring-remotes-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "ring-remotes-unauth-capacity", + "type": "StatisticItem", + "value": "10000" + }, + { + "name": "ring-remotes-unauth-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "ring-servfail-queries-capacity", + "type": "StatisticItem", + "value": "10000" + }, + { + "name": "ring-servfail-queries-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "ring-unauth-queries-capacity", + "type": "StatisticItem", + "value": "10000" + }, + { + "name": "ring-unauth-queries-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "security-status", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "servfail-packets", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "signature-cache-size", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "signatures", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "sys-msec", + "type": "StatisticItem", + "value": "128" + }, + { + "name": "tcp-answers", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "tcp-answers-bytes", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "tcp-queries", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "tcp4-answers", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "tcp4-answers-bytes", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "tcp4-queries", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "tcp6-answers", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "tcp6-answers-bytes", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "tcp6-queries", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "timedout-packets", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp-answers", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp-answers-bytes", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp-do-queries", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp-in-errors", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp-noport-errors", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp-queries", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp-recvbuf-errors", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp-sndbuf-errors", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp4-answers", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp4-answers-bytes", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp4-queries", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp6-answers", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp6-answers-bytes", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "udp6-queries", + "type": "StatisticItem", + "value": "0" + }, + { + "name": "uptime", + "type": "StatisticItem", + "value": "207" + }, + { + "name": "user-msec", + "type": "StatisticItem", + "value": "56" + }, + { + "name": "response-by-qtype", + "type": "MapStatisticItem", + "value": [] + }, + { + "name": "response-sizes", + "type": "MapStatisticItem", + "value": [] + }, + { + "name": "response-by-rcode", + "type": "MapStatisticItem", + "value": [] + }, + { + "name": "logmessages", + "size": "10000", + "type": "RingStatisticItem", + "value": [ + { + "name": "[webserver] 088688d6-9976-4e4d-a6aa-2272f8c6f173 HTTP Request \"/api/v1/servers/localhost/statistics\": Authentication by API Key failed", + "value": "1" + }, + { + "name": "[webserver] 662e4249-4e9a-42e7-b780-b81929875b8f HTTP Request \"/api/v1/servers/localhost/statistics\": Authentication by API Key failed", + "value": "1" + }, + { + "name": "[webserver] 8c79870a-9a47-4952-9166-02710d146ab3 HTTP Request \"/api/v1/servers/localhost/statistics\": Authentication by API Key failed", + "value": "1" + }, + { + "name": "[webserver] dc029119-209f-4101-9e8f-82ab02d857d9 HTTP Request \"/api/v1/servers/localhost/statistics\": Authentication by API Key failed", + "value": "1" + }, + { + "name": "[webserver] fa61f546-8607-4771-bc9a-48ddc5a85dc0 HTTP Request \"/api/v1/servers/localhost/statistics\": Authentication by API Key failed", + "value": "1" + }, + { + "name": "About to create 3 backend threads for UDP", + "value": "1" + }, + { + "name": "Creating backend connection for TCP", + "value": "1" + }, + { + "name": "Done launching threads, ready to distribute questions", + "value": "1" + }, + { + "name": "Master/slave communicator launching", + "value": "1" + }, + { + "name": "No master domains need notifications", + "value": "1" + } + ] + }, + { + "name": "remotes", + "size": "10000", + "type": "RingStatisticItem", + "value": [] + }, + { + "name": "remotes-corrupt", + "size": "10000", + "type": "RingStatisticItem", + "value": [] + }, + { + "name": "remotes-unauth", + "size": "10000", + "type": "RingStatisticItem", + "value": [] + }, + { + "name": "noerror-queries", + "size": "10000", + "type": "RingStatisticItem", + "value": [] + }, + { + "name": "nxdomain-queries", + "size": "10000", + "type": "RingStatisticItem", + "value": [] + }, + { + "name": "queries", + "size": "10000", + "type": "RingStatisticItem", + "value": [] + }, + { + "name": "servfail-queries", + "size": "10000", + "type": "RingStatisticItem", + "value": [] + }, + { + "name": "unauth-queries", + "size": "10000", + "type": "RingStatisticItem", + "value": [] + } +] diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/config.json b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/config.json new file mode 100644 index 000000000..984c3ed6e --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/config.json @@ -0,0 +1,20 @@ +{ + "update_every": 123, + "url": "ok", + "body": "ok", + "method": "ok", + "headers": { + "ok": "ok" + }, + "username": "ok", + "password": "ok", + "proxy_url": "ok", + "proxy_username": "ok", + "proxy_password": "ok", + "timeout": 123.123, + "not_follow_redirects": true, + "tls_ca": "ok", + "tls_cert": "ok", + "tls_key": "ok", + "tls_skip_verify": true +} diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/config.yaml new file mode 100644 index 000000000..8558b61cc --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/config.yaml @@ -0,0 +1,17 @@ +update_every: 123 +url: "ok" +body: "ok" +method: "ok" +headers: + ok: "ok" +username: "ok" +password: "ok" +proxy_url: "ok" +proxy_username: "ok" +proxy_password: "ok" +timeout: 123.123 +not_follow_redirects: yes +tls_ca: "ok" +tls_cert: "ok" +tls_key: "ok" +tls_skip_verify: yes diff --git a/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/v4.3.1/statistics.json b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/v4.3.1/statistics.json new file mode 100644 index 000000000..a31477959 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/powerdns_recursor/testdata/v4.3.1/statistics.json @@ -0,0 +1,587 @@ +[ + { + "name": "all-outqueries", + "type": "StatisticItem", + "value": "41" + }, + { + "name": "answers-slow", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "answers0-1", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "answers1-10", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "answers10-100", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "answers100-1000", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "auth-zone-queries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "auth4-answers-slow", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "auth4-answers0-1", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "auth4-answers1-10", + "type": "StatisticItem", + "value": "5" + }, + { + "name": "auth4-answers10-100", + "type": "StatisticItem", + "value": "35" + }, + { + "name": "auth4-answers100-1000", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "auth6-answers-slow", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "auth6-answers0-1", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "auth6-answers1-10", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "auth6-answers10-100", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "auth6-answers100-1000", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "cache-entries", + "type": "StatisticItem", + "value": "171" + }, + { + "name": "cache-hits", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "cache-misses", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "case-mismatches", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "chain-resends", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "client-parse-errors", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "concurrent-queries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "cpu-msec-thread-0", + "type": "StatisticItem", + "value": "439" + }, + { + "name": "cpu-msec-thread-1", + "type": "StatisticItem", + "value": "445" + }, + { + "name": "cpu-msec-thread-2", + "type": "StatisticItem", + "value": "466" + }, + { + "name": "dlg-only-drops", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "dnssec-authentic-data-queries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "dnssec-check-disabled-queries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "dnssec-queries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "dnssec-result-bogus", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "dnssec-result-indeterminate", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "dnssec-result-insecure", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "dnssec-result-nta", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "dnssec-result-secure", + "type": "StatisticItem", + "value": "5" + }, + { + "name": "dnssec-validations", + "type": "StatisticItem", + "value": "5" + }, + { + "name": "dont-outqueries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "ecs-queries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "ecs-responses", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "edns-ping-matches", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "edns-ping-mismatches", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "empty-queries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "failed-host-entries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "fd-usage", + "type": "StatisticItem", + "value": "32" + }, + { + "name": "ignored-packets", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "ipv6-outqueries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "ipv6-questions", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "malloc-bytes", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "max-cache-entries", + "type": "StatisticItem", + "value": "1000000" + }, + { + "name": "max-mthread-stack", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "max-packetcache-entries", + "type": "StatisticItem", + "value": "500000" + }, + { + "name": "negcache-entries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "no-packet-error", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "noedns-outqueries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "noerror-answers", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "noping-outqueries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "nsset-invalidations", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "nsspeeds-entries", + "type": "StatisticItem", + "value": "78" + }, + { + "name": "nxdomain-answers", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "outgoing-timeouts", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "outgoing4-timeouts", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "outgoing6-timeouts", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "over-capacity-drops", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "packetcache-entries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "packetcache-hits", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "packetcache-misses", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "policy-drops", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "policy-result-custom", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "policy-result-drop", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "policy-result-noaction", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "policy-result-nodata", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "policy-result-nxdomain", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "policy-result-truncate", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "qa-latency", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "qname-min-fallback-success", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "query-pipe-full-drops", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "questions", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "real-memory-usage", + "type": "StatisticItem", + "value": "44773376" + }, + { + "name": "rebalanced-queries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "resource-limits", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "security-status", + "type": "StatisticItem", + "value": "3" + }, + { + "name": "server-parse-errors", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "servfail-answers", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "spoof-prevents", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "sys-msec", + "type": "StatisticItem", + "value": "1520" + }, + { + "name": "tcp-client-overflow", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "tcp-clients", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "tcp-outqueries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "tcp-questions", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "throttle-entries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "throttled-out", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "throttled-outqueries", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "too-old-drops", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "truncated-drops", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "udp-in-errors", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "udp-noport-errors", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "udp-recvbuf-errors", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "udp-sndbuf-errors", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "unauthorized-tcp", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "unauthorized-udp", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "unexpected-packets", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "unreachables", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "uptime", + "type": "StatisticItem", + "value": "1624" + }, + { + "name": "user-msec", + "type": "StatisticItem", + "value": "465" + }, + { + "name": "variable-responses", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "x-our-latency", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "x-ourtime-slow", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "x-ourtime0-1", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "x-ourtime1-2", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "x-ourtime16-32", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "x-ourtime2-4", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "x-ourtime4-8", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "x-ourtime8-16", + "type": "StatisticItem", + "value": "1" + }, + { + "name": "response-by-qtype", + "type": "MapStatisticItem", + "value": [] + }, + { + "name": "response-sizes", + "type": "MapStatisticItem", + "value": [] + }, + { + "name": "response-by-rcode", + "type": "MapStatisticItem", + "value": [] + } +] |