summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/couchdb
diff options
context:
space:
mode:
Diffstat (limited to '')
l---------src/go/collectors/go.d.plugin/modules/couchdb/README.md1
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/charts.go228
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/collect.go244
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/config_schema.json191
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/couchdb.go134
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go464
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/init.go66
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/integrations/couchdb.md225
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/metadata.yaml323
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/metrics.go200
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.json22
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.yaml19
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/active_tasks.json63
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/dbs_info.json52
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_stats.json1651
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_system.json176
-rw-r--r--src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/root.json16
17 files changed, 4075 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/README.md b/src/go/collectors/go.d.plugin/modules/couchdb/README.md
new file mode 120000
index 000000000..14cff4d36
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/README.md
@@ -0,0 +1 @@
+integrations/couchdb.md \ No newline at end of file
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/charts.go b/src/go/collectors/go.d.plugin/modules/couchdb/charts.go
new file mode 100644
index 000000000..3dedec7db
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/charts.go
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchdb
+
+import (
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+)
+
+type (
+ Charts = module.Charts
+ Dims = module.Dims
+ Vars = module.Vars
+)
+
+var dbActivityCharts = Charts{
+ {
+ ID: "activity",
+ Title: "Overall Activity",
+ Units: "requests/s",
+ Fam: "dbactivity",
+ Ctx: "couchdb.activity",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: "couchdb_database_reads", Name: "DB reads", Algo: module.Incremental},
+ {ID: "couchdb_database_writes", Name: "DB writes", Algo: module.Incremental},
+ {ID: "couchdb_httpd_view_reads", Name: "View reads", Algo: module.Incremental},
+ },
+ },
+}
+
+var httpTrafficBreakdownCharts = Charts{
+ {
+ ID: "request_methods",
+ Title: "HTTP request methods",
+ Units: "requests/s",
+ Fam: "httptraffic",
+ Ctx: "couchdb.request_methods",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: "couchdb_httpd_request_methods_COPY", Name: "COPY", Algo: module.Incremental},
+ {ID: "couchdb_httpd_request_methods_DELETE", Name: "DELETE", Algo: module.Incremental},
+ {ID: "couchdb_httpd_request_methods_GET", Name: "GET", Algo: module.Incremental},
+ {ID: "couchdb_httpd_request_methods_HEAD", Name: "HEAD", Algo: module.Incremental},
+ {ID: "couchdb_httpd_request_methods_OPTIONS", Name: "OPTIONS", Algo: module.Incremental},
+ {ID: "couchdb_httpd_request_methods_POST", Name: "POST", Algo: module.Incremental},
+ {ID: "couchdb_httpd_request_methods_PUT", Name: "PUT", Algo: module.Incremental},
+ },
+ },
+ {
+ ID: "response_codes",
+ Title: "HTTP response status codes",
+ Units: "responses/s",
+ Fam: "httptraffic",
+ Ctx: "couchdb.response_codes",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: "couchdb_httpd_status_codes_200", Name: "200 OK", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_201", Name: "201 Created", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_202", Name: "202 Accepted", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_204", Name: "204 No Content", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_206", Name: "206 Partial Content", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_301", Name: "301 Moved Permanently", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_302", Name: "302 Found", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_304", Name: "304 Not Modified", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_400", Name: "400 Bad Request", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_401", Name: "401 Unauthorized", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_403", Name: "403 Forbidden", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_404", Name: "404 Not Found", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_406", Name: "406 Not Acceptable", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_409", Name: "409 Conflict", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_412", Name: "412 Precondition Failed", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_413", Name: "413 Request Entity Too Long", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_414", Name: "414 Request URI Too Long", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_415", Name: "415 Unsupported Media Type", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_416", Name: "416 Requested Range Not Satisfiable", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_417", Name: "417 Expectation Failed", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_500", Name: "500 Internal Server Error", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_501", Name: "501 Not Implemented", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_503", Name: "503 Service Unavailable", Algo: module.Incremental},
+ },
+ },
+ {
+ ID: "response_code_classes",
+ Title: "HTTP response status code classes",
+ Units: "responses/s",
+ Fam: "httptraffic",
+ Ctx: "couchdb.response_code_classes",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: "couchdb_httpd_status_codes_2xx", Name: "2xx Success", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_3xx", Name: "3xx Redirection", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_4xx", Name: "4xx Client error", Algo: module.Incremental},
+ {ID: "couchdb_httpd_status_codes_5xx", Name: "5xx Server error", Algo: module.Incremental},
+ },
+ },
+}
+
+var serverOperationsCharts = Charts{
+ {
+ ID: "active_tasks",
+ Title: "Active task breakdown",
+ Units: "tasks",
+ Fam: "ops",
+ Ctx: "couchdb.active_tasks",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: "active_tasks_indexer", Name: "Indexer"},
+ {ID: "active_tasks_database_compaction", Name: "DB Compaction"},
+ {ID: "active_tasks_replication", Name: "Replication"},
+ {ID: "active_tasks_view_compaction", Name: "View Compaction"},
+ },
+ },
+ {
+ ID: "replicator_jobs",
+ Title: "Replicator job breakdown",
+ Units: "jobs",
+ Fam: "ops",
+ Ctx: "couchdb.replicator_jobs",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: "couch_replicator_jobs_running", Name: "Running"},
+ {ID: "couch_replicator_jobs_pending", Name: "Pending"},
+ {ID: "couch_replicator_jobs_crashed", Name: "Crashed"},
+ {ID: "internal_replication_jobs", Name: "Internal replication jobs"},
+ },
+ },
+ {
+ ID: "open_files",
+ Title: "Open files",
+ Units: "files",
+ Fam: "ops",
+ Ctx: "couchdb.open_files",
+ Dims: Dims{
+ {ID: "couchdb_open_os_files", Name: "# files"},
+ },
+ },
+}
+
+var erlangStatisticsCharts = Charts{
+ {
+ ID: "erlang_memory",
+ Title: "Erlang VM memory usage",
+ Units: "B",
+ Fam: "erlang",
+ Ctx: "couchdb.erlang_vm_memory",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: "memory_atom", Name: "atom"},
+ {ID: "memory_binary", Name: "binaries"},
+ {ID: "memory_code", Name: "code"},
+ {ID: "memory_ets", Name: "ets"},
+ {ID: "memory_processes", Name: "procs"},
+ {ID: "memory_other", Name: "other"},
+ },
+ },
+ {
+ ID: "erlang_proc_counts",
+ Title: "Process counts",
+ Units: "processes",
+ Fam: "erlang",
+ Ctx: "couchdb.proccounts",
+ Dims: Dims{
+ {ID: "os_proc_count", Name: "OS procs"},
+ {ID: "process_count", Name: "erl procs"},
+ },
+ },
+ {
+ ID: "erlang_peak_msg_queue",
+ Title: "Peak message queue size",
+ Units: "messages",
+ Fam: "erlang",
+ Ctx: "couchdb.peakmsgqueue",
+ Dims: Dims{
+ {ID: "peak_msg_queue", Name: "peak size"},
+ },
+ },
+ {
+ ID: "erlang_reductions",
+ Title: "Erlang reductions",
+ Units: "reductions",
+ Fam: "erlang",
+ Ctx: "couchdb.reductions",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: "reductions", Name: "reductions", Algo: module.Incremental},
+ },
+ },
+}
+
+var (
+ dbSpecificCharts = Charts{
+ {
+ ID: "db_sizes_file",
+ Title: "Database sizes (file)",
+ Units: "KiB",
+ Fam: "perdbstats",
+ Ctx: "couchdb.db_sizes_file",
+ },
+ {
+ ID: "db_sizes_external",
+ Title: "Database sizes (external)",
+ Units: "KiB",
+ Fam: "perdbstats",
+ Ctx: "couchdb.db_sizes_external",
+ },
+ {
+ ID: "db_sizes_active",
+ Title: "Database sizes (active)",
+ Units: "KiB",
+ Fam: "perdbstats",
+ Ctx: "couchdb.db_sizes_active",
+ },
+ {
+ ID: "db_doc_counts",
+ Title: "Database # of docs",
+ Units: "docs",
+ Fam: "perdbstats",
+ Ctx: "couchdb.db_doc_count",
+ },
+ {
+ ID: "db_doc_del_counts",
+ Title: "Database # of deleted docs",
+ Units: "docs",
+ Fam: "perdbstats",
+ Ctx: "couchdb.db_doc_del_count",
+ },
+ }
+)
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/collect.go b/src/go/collectors/go.d.plugin/modules/couchdb/collect.go
new file mode 100644
index 000000000..5c722fd0c
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/collect.go
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchdb
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "net/http"
+ "strings"
+ "sync"
+
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/stm"
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/web"
+)
+
+const (
+ urlPathActiveTasks = "/_active_tasks"
+ urlPathOverviewStats = "/_node/%s/_stats"
+ urlPathSystemStats = "/_node/%s/_system"
+ urlPathDatabases = "/_dbs_info"
+
+ httpStatusCodePrefix = "couchdb_httpd_status_codes_"
+ httpStatusCodePrefixLen = len(httpStatusCodePrefix)
+)
+
+func (cdb *CouchDB) collect() (map[string]int64, error) {
+ ms := cdb.scrapeCouchDB()
+ if ms.empty() {
+ return nil, nil
+ }
+
+ collected := make(map[string]int64)
+ cdb.collectNodeStats(collected, ms)
+ cdb.collectSystemStats(collected, ms)
+ cdb.collectActiveTasks(collected, ms)
+ cdb.collectDBStats(collected, ms)
+
+ return collected, nil
+}
+
+func (cdb *CouchDB) collectNodeStats(collected map[string]int64, ms *cdbMetrics) {
+ if !ms.hasNodeStats() {
+ return
+ }
+
+ for metric, value := range stm.ToMap(ms.NodeStats) {
+ collected[metric] = value
+ if strings.HasPrefix(metric, httpStatusCodePrefix) {
+ code := metric[httpStatusCodePrefixLen:]
+ collected["couchdb_httpd_status_codes_"+string(code[0])+"xx"] += value
+ }
+ }
+}
+
+func (cdb *CouchDB) collectSystemStats(collected map[string]int64, ms *cdbMetrics) {
+ if !ms.hasNodeSystem() {
+ return
+ }
+
+ for metric, value := range stm.ToMap(ms.NodeSystem) {
+ collected[metric] = value
+ }
+
+ collected["peak_msg_queue"] = findMaxMQSize(ms.NodeSystem.MessageQueues)
+}
+
+func (cdb *CouchDB) collectActiveTasks(collected map[string]int64, ms *cdbMetrics) {
+ collected["active_tasks_indexer"] = 0
+ collected["active_tasks_database_compaction"] = 0
+ collected["active_tasks_replication"] = 0
+ collected["active_tasks_view_compaction"] = 0
+
+ if !ms.hasActiveTasks() {
+ return
+ }
+
+ for _, task := range ms.ActiveTasks {
+ collected["active_tasks_"+task.Type]++
+ }
+}
+
+func (cdb *CouchDB) collectDBStats(collected map[string]int64, ms *cdbMetrics) {
+ if !ms.hasDBStats() {
+ return
+ }
+
+ for _, dbStats := range ms.DBStats {
+ if dbStats.Error != "" {
+ cdb.Warning("database '", dbStats.Key, "' doesn't exist")
+ continue
+ }
+ merge(collected, stm.ToMap(dbStats.Info), "db_"+dbStats.Key)
+ }
+}
+
+func (cdb *CouchDB) scrapeCouchDB() *cdbMetrics {
+ ms := &cdbMetrics{}
+ wg := &sync.WaitGroup{}
+
+ wg.Add(1)
+ go func() { defer wg.Done(); cdb.scrapeNodeStats(ms) }()
+
+ wg.Add(1)
+ go func() { defer wg.Done(); cdb.scrapeSystemStats(ms) }()
+
+ wg.Add(1)
+ go func() { defer wg.Done(); cdb.scrapeActiveTasks(ms) }()
+
+ if len(cdb.databases) > 0 {
+ wg.Add(1)
+ go func() { defer wg.Done(); cdb.scrapeDBStats(ms) }()
+ }
+
+ wg.Wait()
+ return ms
+}
+
+func (cdb *CouchDB) scrapeNodeStats(ms *cdbMetrics) {
+ req, _ := web.NewHTTPRequest(cdb.Request)
+ req.URL.Path = fmt.Sprintf(urlPathOverviewStats, cdb.Config.Node)
+
+ var stats cdbNodeStats
+ if err := cdb.doOKDecode(req, &stats); err != nil {
+ cdb.Warning(err)
+ return
+ }
+ ms.NodeStats = &stats
+}
+
+func (cdb *CouchDB) scrapeSystemStats(ms *cdbMetrics) {
+ req, _ := web.NewHTTPRequest(cdb.Request)
+ req.URL.Path = fmt.Sprintf(urlPathSystemStats, cdb.Config.Node)
+
+ var stats cdbNodeSystem
+ if err := cdb.doOKDecode(req, &stats); err != nil {
+ cdb.Warning(err)
+ return
+ }
+ ms.NodeSystem = &stats
+}
+
+func (cdb *CouchDB) scrapeActiveTasks(ms *cdbMetrics) {
+ req, _ := web.NewHTTPRequest(cdb.Request)
+ req.URL.Path = urlPathActiveTasks
+
+ var stats []cdbActiveTask
+ if err := cdb.doOKDecode(req, &stats); err != nil {
+ cdb.Warning(err)
+ return
+ }
+ ms.ActiveTasks = stats
+}
+
+func (cdb *CouchDB) scrapeDBStats(ms *cdbMetrics) {
+ req, _ := web.NewHTTPRequest(cdb.Request)
+ req.URL.Path = urlPathDatabases
+ req.Method = http.MethodPost
+ req.Header.Add("Accept", "application/json")
+ req.Header.Add("Content-Type", "application/json")
+
+ var q struct {
+ Keys []string `json:"keys"`
+ }
+ q.Keys = cdb.databases
+ body, err := json.Marshal(q)
+ if err != nil {
+ cdb.Error(err)
+ return
+ }
+ req.Body = io.NopCloser(bytes.NewReader(body))
+
+ var stats []cdbDBStats
+ if err := cdb.doOKDecode(req, &stats); err != nil {
+ cdb.Warning(err)
+ return
+ }
+ ms.DBStats = stats
+}
+
+func findMaxMQSize(MessageQueues map[string]interface{}) int64 {
+ var max float64
+ for _, mq := range MessageQueues {
+ switch mqSize := mq.(type) {
+ case float64:
+ max = math.Max(max, mqSize)
+ case map[string]interface{}:
+ if v, ok := mqSize["count"].(float64); ok {
+ max = math.Max(max, v)
+ }
+ }
+ }
+ return int64(max)
+}
+
+func (cdb *CouchDB) pingCouchDB() error {
+ req, _ := web.NewHTTPRequest(cdb.Request)
+
+ var info struct{ Couchdb string }
+ if err := cdb.doOKDecode(req, &info); err != nil {
+ return err
+ }
+
+ if info.Couchdb != "Welcome" {
+ return errors.New("not a CouchDB endpoint")
+ }
+
+ return nil
+}
+
+func (cdb *CouchDB) doOKDecode(req *http.Request, in interface{}) error {
+ resp, err := cdb.httpClient.Do(req)
+ if err != nil {
+ return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
+ }
+ defer closeBody(resp)
+
+ // TODO: read resp body, it contains reason
+ // ex.: {"error":"bad_request","reason":"`keys` member must exist."} (400)
+ 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()
+ }
+}
+
+func merge(dst, src map[string]int64, prefix string) {
+ for k, v := range src {
+ dst[prefix+"_"+k] = v
+ }
+}
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/config_schema.json b/src/go/collectors/go.d.plugin/modules/couchdb/config_schema.json
new file mode 100644
index 000000000..2f9df722a
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/config_schema.json
@@ -0,0 +1,191 @@
+{
+ "jsonSchema": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "CouchDB 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 CouchDB web server.",
+ "type": "string",
+ "default": "http://127.0.0.1:5984",
+ "format": "uri"
+ },
+ "timeout": {
+ "title": "Timeout",
+ "description": "The timeout in seconds for the HTTP request.",
+ "type": "number",
+ "minimum": 0.5,
+ "default": 2
+ },
+ "not_follow_redirects": {
+ "title": "Not follow redirects",
+ "description": "If set, the client will not follow HTTP redirects automatically.",
+ "type": "boolean"
+ },
+ "node": {
+ "title": "Node name",
+ "description": "CouchDB node name. Same as -name vm.args argument.",
+ "type": "string",
+ "default": "_local"
+ },
+ "databases": {
+ "title": "Databases",
+ "description": "A space-separated list of database names for which you want to collect data. Leave blank to exclude database statistics.",
+ "type": "string"
+ },
+ "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",
+ "node"
+ ],
+ "additionalProperties": false,
+ "patternProperties": {
+ "^name$": {}
+ }
+ },
+ "uiSchema": {
+ "ui:flavour": "tabs",
+ "ui:options": {
+ "tabs": [
+ {
+ "title": "Base",
+ "fields": [
+ "update_every",
+ "url",
+ "timeout",
+ "not_follow_redirects",
+ "node",
+ "databases"
+ ]
+ },
+ {
+ "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/couchdb/couchdb.go b/src/go/collectors/go.d.plugin/modules/couchdb/couchdb.go
new file mode 100644
index 000000000..459251e9d
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/couchdb.go
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchdb
+
+import (
+ _ "embed"
+ "errors"
+ "net/http"
+ "strings"
+ "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("couchdb", module.Creator{
+ JobConfigSchema: configSchema,
+ Defaults: module.Defaults{
+ UpdateEvery: 10,
+ },
+ Create: func() module.Module { return New() },
+ Config: func() any { return &Config{} },
+ })
+}
+
+func New() *CouchDB {
+ return &CouchDB{
+ Config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{
+ URL: "http://127.0.0.1:5984",
+ },
+ Client: web.Client{
+ Timeout: web.Duration(time.Second * 2),
+ },
+ },
+ Node: "_local",
+ },
+ }
+}
+
+type Config struct {
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
+ web.HTTP `yaml:",inline" json:""`
+ Node string `yaml:"node,omitempty" json:"node"`
+ Databases string `yaml:"databases,omitempty" json:"databases"`
+}
+
+type CouchDB struct {
+ module.Base
+ Config `yaml:",inline" json:""`
+
+ charts *module.Charts
+
+ httpClient *http.Client
+
+ databases []string
+}
+
+func (cdb *CouchDB) Configuration() any {
+ return cdb.Config
+}
+
+func (cdb *CouchDB) Init() error {
+ err := cdb.validateConfig()
+ if err != nil {
+ cdb.Errorf("check configuration: %v", err)
+ return err
+ }
+
+ cdb.databases = strings.Fields(cdb.Config.Databases)
+
+ httpClient, err := cdb.initHTTPClient()
+ if err != nil {
+ cdb.Errorf("init HTTP client: %v", err)
+ return err
+ }
+ cdb.httpClient = httpClient
+
+ charts, err := cdb.initCharts()
+ if err != nil {
+ cdb.Errorf("init charts: %v", err)
+ return err
+ }
+ cdb.charts = charts
+
+ return nil
+}
+
+func (cdb *CouchDB) Check() error {
+ if err := cdb.pingCouchDB(); err != nil {
+ cdb.Error(err)
+ return err
+ }
+
+ mx, err := cdb.collect()
+ if err != nil {
+ cdb.Error(err)
+ return err
+ }
+
+ if len(mx) == 0 {
+ return errors.New("no metrics collected")
+ }
+
+ return nil
+}
+
+func (cdb *CouchDB) Charts() *Charts {
+ return cdb.charts
+}
+
+func (cdb *CouchDB) Collect() map[string]int64 {
+ mx, err := cdb.collect()
+ if err != nil {
+ cdb.Error(err)
+ }
+
+ if len(mx) == 0 {
+ return nil
+ }
+ return mx
+}
+
+func (cdb *CouchDB) Cleanup() {
+ if cdb.httpClient == nil {
+ return
+ }
+ cdb.httpClient.CloseIdleConnections()
+}
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go b/src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go
new file mode 100644
index 000000000..15e7aa0a1
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchdb
+
+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")
+
+ dataVer311Root, _ = os.ReadFile("testdata/v3.1.1/root.json")
+ dataVer311ActiveTasks, _ = os.ReadFile("testdata/v3.1.1/active_tasks.json")
+ dataVer311NodeStats, _ = os.ReadFile("testdata/v3.1.1/node_stats.json")
+ dataVer311NodeSystem, _ = os.ReadFile("testdata/v3.1.1/node_system.json")
+ dataVer311DbsInfo, _ = os.ReadFile("testdata/v3.1.1/dbs_info.json")
+)
+
+func Test_testDataIsValid(t *testing.T) {
+ for name, data := range map[string][]byte{
+ "dataConfigJSON": dataConfigJSON,
+ "dataConfigYAML": dataConfigYAML,
+ "dataVer311Root": dataVer311Root,
+ "dataVer311ActiveTasks": dataVer311ActiveTasks,
+ "dataVer311NodeStats": dataVer311NodeStats,
+ "dataVer311NodeSystem": dataVer311NodeSystem,
+ "dataVer311DbsInfo": dataVer311DbsInfo,
+ } {
+ require.NotNil(t, data, name)
+ }
+}
+
+func TestCouchDB_ConfigurationSerialize(t *testing.T) {
+ module.TestConfigurationSerialize(t, &CouchDB{}, dataConfigJSON, dataConfigYAML)
+}
+
+func TestCouchDB_Init(t *testing.T) {
+ tests := map[string]struct {
+ config Config
+ wantNumOfCharts int
+ wantFail bool
+ }{
+ "default": {
+ wantNumOfCharts: numOfCharts(
+ dbActivityCharts,
+ httpTrafficBreakdownCharts,
+ serverOperationsCharts,
+ erlangStatisticsCharts,
+ ),
+ config: New().Config,
+ },
+ "URL not set": {
+ wantFail: true,
+ config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{URL: ""},
+ }},
+ },
+ "invalid TLSCA": {
+ wantFail: true,
+ config: Config{
+ HTTP: web.HTTP{
+ Client: web.Client{
+ TLSConfig: tlscfg.TLSConfig{TLSCA: "testdata/tls"},
+ },
+ }},
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ es := New()
+ es.Config = test.config
+
+ if test.wantFail {
+ assert.Error(t, es.Init())
+ } else {
+ assert.NoError(t, es.Init())
+ assert.Equal(t, test.wantNumOfCharts, len(*es.Charts()))
+ }
+ })
+ }
+}
+
+func TestCouchDB_Check(t *testing.T) {
+ tests := map[string]struct {
+ prepare func(*testing.T) (cdb *CouchDB, cleanup func())
+ wantFail bool
+ }{
+ "valid data": {prepare: prepareCouchDBValidData},
+ "invalid data": {prepare: prepareCouchDBInvalidData, wantFail: true},
+ "404": {prepare: prepareCouchDB404, wantFail: true},
+ "connection refused": {prepare: prepareCouchDBConnectionRefused, wantFail: true},
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ cdb, cleanup := test.prepare(t)
+ defer cleanup()
+
+ if test.wantFail {
+ assert.Error(t, cdb.Check())
+ } else {
+ assert.NoError(t, cdb.Check())
+ }
+ })
+ }
+}
+
+func TestCouchDB_Charts(t *testing.T) {
+ assert.Nil(t, New().Charts())
+}
+
+func TestCouchDB_Cleanup(t *testing.T) {
+ assert.NotPanics(t, New().Cleanup)
+}
+
+func TestCouchDB_Collect(t *testing.T) {
+ tests := map[string]struct {
+ prepare func() *CouchDB
+ wantCollected map[string]int64
+ checkCharts bool
+ }{
+ "all stats": {
+ prepare: func() *CouchDB {
+ cdb := New()
+ cdb.Config.Databases = "db1 db2"
+ return cdb
+ },
+ wantCollected: map[string]int64{
+
+ // node stats
+ "couch_replicator_jobs_crashed": 1,
+ "couch_replicator_jobs_pending": 1,
+ "couch_replicator_jobs_running": 1,
+ "couchdb_database_reads": 1,
+ "couchdb_database_writes": 14,
+ "couchdb_httpd_request_methods_COPY": 1,
+ "couchdb_httpd_request_methods_DELETE": 1,
+ "couchdb_httpd_request_methods_GET": 75544,
+ "couchdb_httpd_request_methods_HEAD": 1,
+ "couchdb_httpd_request_methods_OPTIONS": 1,
+ "couchdb_httpd_request_methods_POST": 15,
+ "couchdb_httpd_request_methods_PUT": 3,
+ "couchdb_httpd_status_codes_200": 75294,
+ "couchdb_httpd_status_codes_201": 15,
+ "couchdb_httpd_status_codes_202": 1,
+ "couchdb_httpd_status_codes_204": 1,
+ "couchdb_httpd_status_codes_206": 1,
+ "couchdb_httpd_status_codes_301": 1,
+ "couchdb_httpd_status_codes_302": 1,
+ "couchdb_httpd_status_codes_304": 1,
+ "couchdb_httpd_status_codes_400": 1,
+ "couchdb_httpd_status_codes_401": 20,
+ "couchdb_httpd_status_codes_403": 1,
+ "couchdb_httpd_status_codes_404": 225,
+ "couchdb_httpd_status_codes_405": 1,
+ "couchdb_httpd_status_codes_406": 1,
+ "couchdb_httpd_status_codes_409": 1,
+ "couchdb_httpd_status_codes_412": 3,
+ "couchdb_httpd_status_codes_413": 1,
+ "couchdb_httpd_status_codes_414": 1,
+ "couchdb_httpd_status_codes_415": 1,
+ "couchdb_httpd_status_codes_416": 1,
+ "couchdb_httpd_status_codes_417": 1,
+ "couchdb_httpd_status_codes_500": 1,
+ "couchdb_httpd_status_codes_501": 1,
+ "couchdb_httpd_status_codes_503": 1,
+ "couchdb_httpd_status_codes_2xx": 75312,
+ "couchdb_httpd_status_codes_3xx": 3,
+ "couchdb_httpd_status_codes_4xx": 258,
+ "couchdb_httpd_status_codes_5xx": 3,
+ "couchdb_httpd_view_reads": 1,
+ "couchdb_open_os_files": 1,
+
+ // node system
+ "context_switches": 22614499,
+ "ets_table_count": 116,
+ "internal_replication_jobs": 1,
+ "io_input": 49674812,
+ "io_output": 686400800,
+ "memory_atom_used": 488328,
+ "memory_atom": 504433,
+ "memory_binary": 297696,
+ "memory_code": 11252688,
+ "memory_ets": 1579120,
+ "memory_other": 20427855,
+ "memory_processes": 9161448,
+ "os_proc_count": 1,
+ "peak_msg_queue": 2,
+ "process_count": 296,
+ "reductions": 43211228312,
+ "run_queue": 1,
+
+ // active tasks
+ "active_tasks_database_compaction": 1,
+ "active_tasks_indexer": 2,
+ "active_tasks_replication": 1,
+ "active_tasks_view_compaction": 1,
+
+ // databases
+ "db_db1_db_doc_counts": 14,
+ "db_db1_db_doc_del_counts": 1,
+ "db_db1_db_sizes_active": 2818,
+ "db_db1_db_sizes_external": 588,
+ "db_db1_db_sizes_file": 74115,
+
+ "db_db2_db_doc_counts": 15,
+ "db_db2_db_doc_del_counts": 1,
+ "db_db2_db_sizes_active": 1818,
+ "db_db2_db_sizes_external": 288,
+ "db_db2_db_sizes_file": 7415,
+ },
+ checkCharts: true,
+ },
+ "wrong node": {
+ prepare: func() *CouchDB {
+ cdb := New()
+ cdb.Config.Node = "bad_node@bad_host"
+ cdb.Config.Databases = "db1 db2"
+ return cdb
+ },
+ wantCollected: map[string]int64{
+
+ // node stats
+
+ // node system
+
+ // active tasks
+ "active_tasks_database_compaction": 1,
+ "active_tasks_indexer": 2,
+ "active_tasks_replication": 1,
+ "active_tasks_view_compaction": 1,
+
+ // databases
+ "db_db1_db_doc_counts": 14,
+ "db_db1_db_doc_del_counts": 1,
+ "db_db1_db_sizes_active": 2818,
+ "db_db1_db_sizes_external": 588,
+ "db_db1_db_sizes_file": 74115,
+
+ "db_db2_db_doc_counts": 15,
+ "db_db2_db_doc_del_counts": 1,
+ "db_db2_db_sizes_active": 1818,
+ "db_db2_db_sizes_external": 288,
+ "db_db2_db_sizes_file": 7415,
+ },
+ checkCharts: false,
+ },
+ "wrong database": {
+ prepare: func() *CouchDB {
+ cdb := New()
+ cdb.Config.Databases = "bad_db db1 db2"
+ return cdb
+ },
+ wantCollected: map[string]int64{
+
+ // node stats
+ "couch_replicator_jobs_crashed": 1,
+ "couch_replicator_jobs_pending": 1,
+ "couch_replicator_jobs_running": 1,
+ "couchdb_database_reads": 1,
+ "couchdb_database_writes": 14,
+ "couchdb_httpd_request_methods_COPY": 1,
+ "couchdb_httpd_request_methods_DELETE": 1,
+ "couchdb_httpd_request_methods_GET": 75544,
+ "couchdb_httpd_request_methods_HEAD": 1,
+ "couchdb_httpd_request_methods_OPTIONS": 1,
+ "couchdb_httpd_request_methods_POST": 15,
+ "couchdb_httpd_request_methods_PUT": 3,
+ "couchdb_httpd_status_codes_200": 75294,
+ "couchdb_httpd_status_codes_201": 15,
+ "couchdb_httpd_status_codes_202": 1,
+ "couchdb_httpd_status_codes_204": 1,
+ "couchdb_httpd_status_codes_206": 1,
+ "couchdb_httpd_status_codes_301": 1,
+ "couchdb_httpd_status_codes_302": 1,
+ "couchdb_httpd_status_codes_304": 1,
+ "couchdb_httpd_status_codes_400": 1,
+ "couchdb_httpd_status_codes_401": 20,
+ "couchdb_httpd_status_codes_403": 1,
+ "couchdb_httpd_status_codes_404": 225,
+ "couchdb_httpd_status_codes_405": 1,
+ "couchdb_httpd_status_codes_406": 1,
+ "couchdb_httpd_status_codes_409": 1,
+ "couchdb_httpd_status_codes_412": 3,
+ "couchdb_httpd_status_codes_413": 1,
+ "couchdb_httpd_status_codes_414": 1,
+ "couchdb_httpd_status_codes_415": 1,
+ "couchdb_httpd_status_codes_416": 1,
+ "couchdb_httpd_status_codes_417": 1,
+ "couchdb_httpd_status_codes_500": 1,
+ "couchdb_httpd_status_codes_501": 1,
+ "couchdb_httpd_status_codes_503": 1,
+ "couchdb_httpd_status_codes_2xx": 75312,
+ "couchdb_httpd_status_codes_3xx": 3,
+ "couchdb_httpd_status_codes_4xx": 258,
+ "couchdb_httpd_status_codes_5xx": 3,
+ "couchdb_httpd_view_reads": 1,
+ "couchdb_open_os_files": 1,
+
+ // node system
+ "context_switches": 22614499,
+ "ets_table_count": 116,
+ "internal_replication_jobs": 1,
+ "io_input": 49674812,
+ "io_output": 686400800,
+ "memory_atom_used": 488328,
+ "memory_atom": 504433,
+ "memory_binary": 297696,
+ "memory_code": 11252688,
+ "memory_ets": 1579120,
+ "memory_other": 20427855,
+ "memory_processes": 9161448,
+ "os_proc_count": 1,
+ "peak_msg_queue": 2,
+ "process_count": 296,
+ "reductions": 43211228312,
+ "run_queue": 1,
+
+ // active tasks
+ "active_tasks_database_compaction": 1,
+ "active_tasks_indexer": 2,
+ "active_tasks_replication": 1,
+ "active_tasks_view_compaction": 1,
+
+ // databases
+ "db_db1_db_doc_counts": 14,
+ "db_db1_db_doc_del_counts": 1,
+ "db_db1_db_sizes_active": 2818,
+ "db_db1_db_sizes_external": 588,
+ "db_db1_db_sizes_file": 74115,
+
+ "db_db2_db_doc_counts": 15,
+ "db_db2_db_doc_del_counts": 1,
+ "db_db2_db_sizes_active": 1818,
+ "db_db2_db_sizes_external": 288,
+ "db_db2_db_sizes_file": 7415,
+ },
+ checkCharts: false,
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ cdb, cleanup := prepareCouchDB(t, test.prepare)
+ defer cleanup()
+
+ var collected map[string]int64
+ for i := 0; i < 10; i++ {
+ collected = cdb.Collect()
+ }
+
+ assert.Equal(t, test.wantCollected, collected)
+ if test.checkCharts {
+ ensureCollectedHasAllChartsDimsVarsIDs(t, cdb, collected)
+ }
+ })
+ }
+}
+
+func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, cdb *CouchDB, collected map[string]int64) {
+ for _, chart := range *cdb.Charts() {
+ if chart.Obsolete {
+ continue
+ }
+ for _, dim := range chart.Dims {
+ _, ok := collected[dim.ID]
+ assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID)
+ }
+ for _, v := range chart.Vars {
+ _, ok := collected[v.ID]
+ assert.Truef(t, ok, "collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID)
+ }
+ }
+}
+
+func prepareCouchDB(t *testing.T, createCDB func() *CouchDB) (cdb *CouchDB, cleanup func()) {
+ t.Helper()
+ cdb = createCDB()
+ srv := prepareCouchDBEndpoint()
+ cdb.URL = srv.URL
+
+ require.NoError(t, cdb.Init())
+
+ return cdb, srv.Close
+}
+
+func prepareCouchDBValidData(t *testing.T) (cdb *CouchDB, cleanup func()) {
+ return prepareCouchDB(t, New)
+}
+
+func prepareCouchDBInvalidData(t *testing.T) (*CouchDB, func()) {
+ t.Helper()
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ _, _ = w.Write([]byte("hello and\n goodbye"))
+ }))
+ cdb := New()
+ cdb.URL = srv.URL
+ require.NoError(t, cdb.Init())
+
+ return cdb, srv.Close
+}
+
+func prepareCouchDB404(t *testing.T) (*CouchDB, func()) {
+ t.Helper()
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusNotFound)
+ }))
+ cdb := New()
+ cdb.URL = srv.URL
+ require.NoError(t, cdb.Init())
+
+ return cdb, srv.Close
+}
+
+func prepareCouchDBConnectionRefused(t *testing.T) (*CouchDB, func()) {
+ t.Helper()
+ cdb := New()
+ cdb.URL = "http://127.0.0.1:38001"
+ require.NoError(t, cdb.Init())
+
+ return cdb, func() {}
+}
+
+func prepareCouchDBEndpoint() *httptest.Server {
+ return httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ switch r.URL.Path {
+ case "/_node/_local/_stats":
+ _, _ = w.Write(dataVer311NodeStats)
+ case "/_node/_local/_system":
+ _, _ = w.Write(dataVer311NodeSystem)
+ case urlPathActiveTasks:
+ _, _ = w.Write(dataVer311ActiveTasks)
+ case "/_dbs_info":
+ _, _ = w.Write(dataVer311DbsInfo)
+ case "/":
+ _, _ = w.Write(dataVer311Root)
+ default:
+ w.WriteHeader(http.StatusNotFound)
+ }
+ }))
+}
+
+func numOfCharts(charts ...Charts) (num int) {
+ for _, v := range charts {
+ num += len(v)
+ }
+ return num
+}
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/init.go b/src/go/collectors/go.d.plugin/modules/couchdb/init.go
new file mode 100644
index 000000000..028848152
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/init.go
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchdb
+
+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 (cdb *CouchDB) validateConfig() error {
+ if cdb.URL == "" {
+ return errors.New("URL not set")
+ }
+ if cdb.Node == "" {
+ return errors.New("'node' not set")
+ }
+ if _, err := web.NewHTTPRequest(cdb.Request); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (cdb *CouchDB) initHTTPClient() (*http.Client, error) {
+ return web.NewHTTPClient(cdb.Client)
+}
+
+func (cdb *CouchDB) initCharts() (*Charts, error) {
+ charts := module.Charts{}
+
+ if err := charts.Add(*dbActivityCharts.Copy()...); err != nil {
+ return nil, err
+ }
+ if err := charts.Add(*httpTrafficBreakdownCharts.Copy()...); err != nil {
+ return nil, err
+ }
+ if err := charts.Add(*serverOperationsCharts.Copy()...); err != nil {
+ return nil, err
+ }
+ if len(cdb.databases) != 0 {
+ dbCharts := dbSpecificCharts.Copy()
+
+ if err := charts.Add(*dbCharts...); err != nil {
+ return nil, err
+ }
+
+ for _, chart := range *dbCharts {
+ for _, db := range cdb.databases {
+ if err := chart.AddDim(&module.Dim{ID: "db_" + db + "_" + chart.ID, Name: db}); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ }
+ if err := charts.Add(*erlangStatisticsCharts.Copy()...); err != nil {
+ return nil, err
+ }
+
+ if len(charts) == 0 {
+ return nil, errors.New("zero charts")
+ }
+ return &charts, nil
+}
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/integrations/couchdb.md b/src/go/collectors/go.d.plugin/modules/couchdb/integrations/couchdb.md
new file mode 100644
index 000000000..8a59d181e
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/integrations/couchdb.md
@@ -0,0 +1,225 @@
+<!--startmeta
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/couchdb/README.md"
+meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/couchdb/metadata.yaml"
+sidebar_label: "CouchDB"
+learn_status: "Published"
+learn_rel_path: "Collecting Metrics/Databases"
+most_popular: False
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
+endmeta-->
+
+# CouchDB
+
+
+<img src="https://netdata.cloud/img/couchdb.svg" width="150"/>
+
+
+Plugin: go.d.plugin
+Module: couchdb
+
+<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />
+
+## Overview
+
+This collector monitors CouchDB servers.
+
+
+
+
+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 CouchDB instance
+
+These metrics refer to the entire monitored application.
+
+This scope has no labels.
+
+Metrics:
+
+| Metric | Dimensions | Unit |
+|:------|:----------|:----|
+| couchdb.activity | db_reads, db_writes, view_reads | requests/s |
+| couchdb.request_methods | copy, delete, get, head, options, post, put | requests/s |
+| couchdb.response_codes | 200, 201, 202, 204, 206, 301, 302, 304, 400, 401, 403, 404, 406, 409, 412, 413, 414, 415, 416, 417, 500, 501, 503 | responses/s |
+| couchdb.response_code_classes | 2xx, 3xx, 4xx, 5xx | responses/s |
+| couchdb.active_tasks | indexer, db_compaction, replication, view_compaction | tasks |
+| couchdb.replicator_jobs | running, pending, crashed, internal_replication_jobs | jobs |
+| couchdb.open_files | files | files |
+| couchdb.erlang_vm_memory | atom, binaries, code, ets, procs, other | B |
+| couchdb.proccounts | os_procs, erl_procs | processes |
+| couchdb.peakmsgqueue | peak_size | messages |
+| couchdb.reductions | reductions | reductions |
+| couchdb.db_sizes_file | a dimension per database | KiB |
+| couchdb.db_sizes_external | a dimension per database | KiB |
+| couchdb.db_sizes_active | a dimension per database | KiB |
+| couchdb.db_doc_count | a dimension per database | docs |
+| couchdb.db_doc_del_count | a dimension per database | docs |
+
+
+
+## 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/couchdb.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/couchdb.conf
+```
+#### Options
+
+The following options can be defined globally: update_every, autodetection_retry.
+
+
+<details open><summary>Config</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 |
+| url | Server URL. | http://127.0.0.1:5984 | yes |
+| node | CouchDB node name. Same as -name vm.args argument. | _local | no |
+| databases | List of database names for which db-specific stats should be displayed, space separated. | | 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 |
+| timeout | HTTP request timeout. | 2 | 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:5984
+
+```
+</details>
+
+##### Basic HTTP auth
+
+Local server with basic HTTP authentication, node name and multiple databases defined. Make sure to match the node name with the `NODENAME` value in your CouchDB's `etc/vm.args` file. Typically, this is of the form `couchdb@fully.qualified.domain.name` in a cluster, or `couchdb@127.0.0.1` for a single-node server.
+
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ url: http://127.0.0.1:5984
+ node: couchdb@127.0.0.1
+ databases: my-db other-db
+ username: foo
+ password: bar
+
+```
+</details>
+
+##### Multi-instance
+
+> **Note**: When you define multiple jobs, their names must be unique.
+
+Collecting metrics from local and remote instances.
+
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ url: http://127.0.0.1:5984
+
+ - name: remote
+ url: http://203.0.113.0:5984
+
+```
+</details>
+
+
+
+## Troubleshooting
+
+### Debug Mode
+
+To troubleshoot issues with the `couchdb` 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 couchdb
+ ```
+
+
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/metadata.yaml b/src/go/collectors/go.d.plugin/modules/couchdb/metadata.yaml
new file mode 100644
index 000000000..2f0036db2
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/metadata.yaml
@@ -0,0 +1,323 @@
+plugin_name: go.d.plugin
+modules:
+ - meta:
+ id: collector-go.d.plugin-couchdb
+ plugin_name: go.d.plugin
+ module_name: couchdb
+ monitored_instance:
+ name: CouchDB
+ link: https://couchdb.apache.org/
+ icon_filename: couchdb.svg
+ categories:
+ - data-collection.database-servers
+ keywords:
+ - couchdb
+ - databases
+ related_resources:
+ integrations:
+ list: []
+ info_provided_to_referring_integrations:
+ description: ""
+ most_popular: false
+ overview:
+ data_collection:
+ metrics_description: |
+ This collector monitors CouchDB servers.
+ 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/couchdb.conf
+ options:
+ description: |
+ The following options can be defined globally: update_every, autodetection_retry.
+ folding:
+ title: Config
+ 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: url
+ description: Server URL.
+ default_value: http://127.0.0.1:5984
+ required: true
+ - name: node
+ description: CouchDB node name. Same as -name vm.args argument.
+ default_value: "_local"
+ required: false
+ - name: databases
+ description: List of database names for which db-specific stats should be displayed, space separated.
+ default_value: ""
+ 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: timeout
+ description: HTTP request timeout.
+ default_value: 2
+ 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.
+ folding:
+ title: Example
+ enabled: true
+ config: |
+ jobs:
+ - name: local
+ url: http://127.0.0.1:5984
+ - name: Basic HTTP auth
+ description: >
+ Local server with basic HTTP authentication, node name and multiple databases defined.
+ Make sure to match the node name with the `NODENAME` value in your CouchDB's `etc/vm.args` file.
+ Typically, this is of the form `couchdb@fully.qualified.domain.name` in a cluster, or `couchdb@127.0.0.1` for a single-node server.
+ config: |
+ jobs:
+ - name: local
+ url: http://127.0.0.1:5984
+ node: couchdb@127.0.0.1
+ databases: my-db other-db
+ username: foo
+ password: bar
+ - name: Multi-instance
+ description: |
+ > **Note**: When you define multiple jobs, their names must be unique.
+
+ Collecting metrics from local and remote instances.
+ config: |
+ jobs:
+ - name: local
+ url: http://127.0.0.1:5984
+
+ - name: remote
+ url: http://203.0.113.0:5984
+ 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: couchdb.activity
+ description: Overall Activity
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: db_reads
+ - name: db_writes
+ - name: view_reads
+ - name: couchdb.request_methods
+ description: HTTP request methods
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: copy
+ - name: delete
+ - name: get
+ - name: head
+ - name: options
+ - name: post
+ - name: put
+ - name: couchdb.response_codes
+ description: HTTP response status codes
+ unit: responses/s
+ chart_type: stacked
+ dimensions:
+ - name: "200"
+ - name: "201"
+ - name: "202"
+ - name: "204"
+ - name: "206"
+ - name: "301"
+ - name: "302"
+ - name: "304"
+ - name: "400"
+ - name: "401"
+ - name: "403"
+ - name: "404"
+ - name: "406"
+ - name: "409"
+ - name: "412"
+ - name: "413"
+ - name: "414"
+ - name: "415"
+ - name: "416"
+ - name: "417"
+ - name: "500"
+ - name: "501"
+ - name: "503"
+ - name: couchdb.response_code_classes
+ description: HTTP response status code classes
+ unit: responses/s
+ chart_type: stacked
+ dimensions:
+ - name: 2xx
+ - name: 3xx
+ - name: 4xx
+ - name: 5xx
+ - name: couchdb.active_tasks
+ description: Active task breakdown
+ unit: tasks
+ chart_type: stacked
+ dimensions:
+ - name: indexer
+ - name: db_compaction
+ - name: replication
+ - name: view_compaction
+ - name: couchdb.replicator_jobs
+ description: Replicator job breakdown
+ unit: jobs
+ chart_type: stacked
+ dimensions:
+ - name: running
+ - name: pending
+ - name: crashed
+ - name: internal_replication_jobs
+ - name: couchdb.open_files
+ description: Open files
+ unit: files
+ chart_type: line
+ dimensions:
+ - name: files
+ - name: couchdb.erlang_vm_memory
+ description: Erlang VM memory usage
+ unit: B
+ chart_type: stacked
+ dimensions:
+ - name: atom
+ - name: binaries
+ - name: code
+ - name: ets
+ - name: procs
+ - name: other
+ - name: couchdb.proccounts
+ description: Process counts
+ unit: processes
+ chart_type: line
+ dimensions:
+ - name: os_procs
+ - name: erl_procs
+ - name: couchdb.peakmsgqueue
+ description: Peak message queue size
+ unit: messages
+ chart_type: line
+ dimensions:
+ - name: peak_size
+ - name: couchdb.reductions
+ description: Erlang reductions
+ unit: reductions
+ chart_type: line
+ dimensions:
+ - name: reductions
+ - name: couchdb.db_sizes_file
+ description: Database sizes (file)
+ unit: KiB
+ chart_type: line
+ dimensions:
+ - name: a dimension per database
+ - name: couchdb.db_sizes_external
+ description: Database sizes (external)
+ unit: KiB
+ chart_type: line
+ dimensions:
+ - name: a dimension per database
+ - name: couchdb.db_sizes_active
+ description: Database sizes (active)
+ unit: KiB
+ chart_type: line
+ dimensions:
+ - name: a dimension per database
+ - name: couchdb.db_doc_count
+ description: 'Database # of docs'
+ unit: docs
+ chart_type: line
+ dimensions:
+ - name: a dimension per database
+ - name: couchdb.db_doc_del_count
+ description: 'Database # of deleted docs'
+ unit: docs
+ chart_type: line
+ dimensions:
+ - name: a dimension per database
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/metrics.go b/src/go/collectors/go.d.plugin/modules/couchdb/metrics.go
new file mode 100644
index 000000000..4d2f02679
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/metrics.go
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchdb
+
+// https://docs.couchdb.org/en/stable/api/index.html
+
+type cdbMetrics struct {
+ // https://docs.couchdb.org/en/stable/api/server/common.html#active-tasks
+ ActiveTasks []cdbActiveTask
+ // https://docs.couchdb.org/en/stable/api/server/common.html#node-node-name-stats
+ NodeStats *cdbNodeStats
+ // https://docs.couchdb.org/en/stable/api/server/common.html#node-node-name-system
+ NodeSystem *cdbNodeSystem
+ // https://docs.couchdb.org/en/stable/api/database/common.html
+ DBStats []cdbDBStats
+}
+
+func (m cdbMetrics) empty() bool {
+ switch {
+ case m.hasActiveTasks(), m.hasNodeStats(), m.hasNodeSystem(), m.hasDBStats():
+ return false
+ }
+ return true
+}
+
+func (m cdbMetrics) hasActiveTasks() bool { return m.ActiveTasks != nil }
+func (m cdbMetrics) hasNodeStats() bool { return m.NodeStats != nil }
+func (m cdbMetrics) hasNodeSystem() bool { return m.NodeSystem != nil }
+func (m cdbMetrics) hasDBStats() bool { return m.DBStats != nil }
+
+type cdbActiveTask struct {
+ Type string `json:"type"`
+}
+
+type cdbNodeStats struct {
+ CouchDB struct {
+ DatabaseReads struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"database_reads" json:"database_reads"`
+ DatabaseWrites struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"database_writes" json:"database_writes"`
+ HTTPd struct {
+ ViewReads struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"view_reads" json:"view_reads"`
+ } `stm:"httpd" json:"httpd"`
+ HTTPdRequestMethods struct {
+ Copy struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"COPY" json:"COPY"`
+ Delete struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"DELETE" json:"DELETE"`
+ Get struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"GET" json:"GET"`
+ Head struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"HEAD" json:"HEAD"`
+ Options struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"OPTIONS" json:"OPTIONS"`
+ Post struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"POST" json:"POST"`
+ Put struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"PUT" json:"PUT"`
+ } `stm:"httpd_request_methods" json:"httpd_request_methods"`
+ HTTPdStatusCodes struct {
+ Code200 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"200" json:"200"`
+ Code201 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"201" json:"201"`
+ Code202 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"202" json:"202"`
+ Code204 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"204" json:"204"`
+ Code206 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"206" json:"206"`
+ Code301 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"301" json:"301"`
+ Code302 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"302" json:"302"`
+ Code304 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"304" json:"304"`
+ Code400 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"400" json:"400"`
+ Code401 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"401" json:"401"`
+ Code403 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"403" json:"403"`
+ Code404 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"404" json:"404"`
+ Code405 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"405" json:"405"`
+ Code406 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"406" json:"406"`
+ Code409 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"409" json:"409"`
+ Code412 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"412" json:"412"`
+ Code413 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"413" json:"413"`
+ Code414 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"414" json:"414"`
+ Code415 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"415" json:"415"`
+ Code416 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"416" json:"416"`
+ Code417 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"417" json:"417"`
+ Code500 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"500" json:"500"`
+ Code501 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"501" json:"501"`
+ Code503 struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"503" json:"503"`
+ } `stm:"httpd_status_codes" json:"httpd_status_codes"`
+ OpenOSFiles struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"open_os_files" json:"open_os_files"`
+ } `stm:"couchdb" json:"couchdb"`
+ CouchReplicator struct {
+ Jobs struct {
+ Running struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"running" json:"running"`
+ Pending struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"pending" json:"pending"`
+ Crashed struct {
+ Value float64 `stm:"" json:"value"`
+ } `stm:"crashed" json:"crashed"`
+ } `stm:"jobs" json:"jobs"`
+ } `stm:"couch_replicator" json:"couch_replicator"`
+}
+
+type cdbNodeSystem struct {
+ Memory struct {
+ Other float64 `stm:"other" json:"other"`
+ Atom float64 `stm:"atom" json:"atom"`
+ AtomUsed float64 `stm:"atom_used" json:"atom_used"`
+ Processes float64 `stm:"processes" json:"processes"`
+ Binary float64 `stm:"binary" json:"binary"`
+ Code float64 `stm:"code" json:"code"`
+ Ets float64 `stm:"ets" json:"ets"`
+ } `stm:"memory" json:"memory"`
+
+ RunQueue float64 `stm:"run_queue" json:"run_queue"`
+ EtsTableCount float64 `stm:"ets_table_count" json:"ets_table_count"`
+ ContextSwitches float64 `stm:"context_switches" json:"context_switches"`
+ Reductions float64 `stm:"reductions" json:"reductions"`
+ IOInput float64 `stm:"io_input" json:"io_input"`
+ IOOutput float64 `stm:"io_output" json:"io_output"`
+ OSProcCount float64 `stm:"os_proc_count" json:"os_proc_count"`
+ ProcessCount float64 `stm:"process_count" json:"process_count"`
+ InternalReplicationJobs float64 `stm:"internal_replication_jobs" json:"internal_replication_jobs"`
+
+ MessageQueues map[string]interface{} `json:"message_queues"`
+}
+
+type cdbDBStats struct {
+ Key string
+ Error string
+ Info struct {
+ Sizes struct {
+ File float64 `stm:"file" json:"file"`
+ External float64 `stm:"external" json:"external"`
+ Active float64 `stm:"active" json:"active"`
+ } `stm:"db_sizes" json:"sizes"`
+ DocDelCount float64 `stm:"db_doc_del_counts" json:"doc_del_count"`
+ DocCount float64 `stm:"db_doc_counts" json:"doc_count"`
+ }
+}
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.json
new file mode 100644
index 000000000..0fa716e5d
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.json
@@ -0,0 +1,22 @@
+{
+ "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,
+ "node": "ok",
+ "databases": "ok"
+}
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.yaml
new file mode 100644
index 000000000..4968ed263
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.yaml
@@ -0,0 +1,19 @@
+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
+node: "ok"
+databases: "ok"
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/active_tasks.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/active_tasks.json
new file mode 100644
index 000000000..788fe5642
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/active_tasks.json
@@ -0,0 +1,63 @@
+[
+ {
+ "changes_done": 64438,
+ "database": "mailbox",
+ "pid": "<0.12986.1>",
+ "progress": 84,
+ "started_on": 1376116576,
+ "total_changes": 76215,
+ "type": "database_compaction",
+ "updated_on": 1376116619
+ },
+ {
+ "changes_done": 26534,
+ "database": "mailbox",
+ "pid": "<0.12943.2>",
+ "progress": 23,
+ "started_on": 1376116592,
+ "total_changes": 76215,
+ "type": "view_compaction",
+ "updated_on": 1376116637
+ },
+ {
+ "changes_done": 14443,
+ "database": "mailbox",
+ "design_document": "c9753817b3ba7c674d92361f24f59b9f",
+ "pid": "<0.10461.3>",
+ "progress": 18,
+ "started_on": 1376116621,
+ "total_changes": 76215,
+ "type": "indexer",
+ "updated_on": 1376116650
+ },
+ {
+ "changes_done": 5454,
+ "database": "mailbox",
+ "design_document": "_design/meta",
+ "pid": "<0.6838.4>",
+ "progress": 7,
+ "started_on": 1376116632,
+ "total_changes": 76215,
+ "type": "indexer",
+ "updated_on": 1376116651
+ },
+ {
+ "checkpointed_source_seq": 68585,
+ "continuous": false,
+ "doc_id": null,
+ "doc_write_failures": 1,
+ "docs_read": 4524,
+ "docs_written": 4524,
+ "missing_revisions_found": 4524,
+ "pid": "<0.1538.5>",
+ "progress": 44,
+ "replication_id": "9bc1727d74d49d9e157e260bb8bbd1d5",
+ "revisions_checked": 4524,
+ "source": "mailbox",
+ "source_seq": 154419,
+ "started_on": 1376116644,
+ "target": "http://mailsrv:5984/mailbox",
+ "type": "replication",
+ "updated_on": 1376116651
+ }
+]
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/dbs_info.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/dbs_info.json
new file mode 100644
index 000000000..9ca43a53c
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/dbs_info.json
@@ -0,0 +1,52 @@
+[
+ {
+ "key": "db1",
+ "info": {
+ "db_name": "db1",
+ "purge_seq": "0-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEhlwqEtkSKqHKMgCAIT2GV4",
+ "update_seq": "14-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEjlxqEtkSKoHK2DNAgCGOxls",
+ "sizes": {
+ "file": 74115,
+ "external": 588,
+ "active": 2818
+ },
+ "props": {},
+ "doc_del_count": 1,
+ "doc_count": 14,
+ "disk_format_version": 8,
+ "compact_running": false,
+ "cluster": {
+ "q": 2,
+ "n": 1,
+ "w": 1,
+ "r": 1
+ },
+ "instance_start_time": "0"
+ }
+ },
+ {
+ "key": "db2",
+ "info": {
+ "db_name": "db2",
+ "purge_seq": "0-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEhlwqEtkSKqHKMgCAIT2GV5",
+ "update_seq": "14-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEjlxqEtkSKoHK2DNAgCGOxlt",
+ "sizes": {
+ "file": 7415,
+ "external": 288,
+ "active": 1818
+ },
+ "props": {},
+ "doc_del_count": 1,
+ "doc_count": 15,
+ "disk_format_version": 8,
+ "compact_running": false,
+ "cluster": {
+ "q": 2,
+ "n": 1,
+ "w": 1,
+ "r": 1
+ },
+ "instance_start_time": "0"
+ }
+ }
+]
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_stats.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_stats.json
new file mode 100644
index 000000000..ae31366af
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_stats.json
@@ -0,0 +1,1651 @@
+{
+ "global_changes": {
+ "db_writes": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of db writes performed by global changes"
+ },
+ "event_doc_conflict": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of conflicted event docs encountered by global changes"
+ },
+ "listener_pending_updates": {
+ "value": 1,
+ "type": "gauge",
+ "desc": "number of global changes updates pending writes in global_changes_listener"
+ },
+ "rpcs": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of rpc operations performed by global_changes"
+ },
+ "server_pending_updates": {
+ "value": 1,
+ "type": "gauge",
+ "desc": "number of global changes updates pending writes in global_changes_server"
+ }
+ },
+ "couchdb": {
+ "httpd": {
+ "aborted_requests": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of aborted requests"
+ },
+ "bulk_docs": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "distribution of the number of docs in _bulk_docs requests"
+ },
+ "bulk_requests": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of bulk requests"
+ },
+ "requests": {
+ "value": 75562,
+ "type": "counter",
+ "desc": "number of HTTP requests"
+ },
+ "view_timeouts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP view timeouts"
+ },
+ "find_timeouts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP find timeouts"
+ },
+ "explain_timeouts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP _explain timeouts"
+ },
+ "all_docs_timeouts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP all_docs timeouts"
+ },
+ "partition_view_requests": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of partition HTTP view requests"
+ },
+ "partition_find_requests": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of partition HTTP _find requests"
+ },
+ "partition_explain_requests": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of partition HTTP _explain requests"
+ },
+ "partition_all_docs_requests": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of partition HTTP _all_docs requests"
+ },
+ "partition_view_timeouts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of partition HTTP view timeouts"
+ },
+ "partition_find_timeouts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of partition HTTP find timeouts"
+ },
+ "partition_explain_timeouts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of partition HTTP _explain timeouts"
+ },
+ "partition_all_docs_timeouts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of partition HTTP all_docs timeouts"
+ },
+ "temporary_view_reads": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of temporary view reads"
+ },
+ "view_reads": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of view reads"
+ },
+ "clients_requesting_changes": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of clients for continuous _changes"
+ },
+ "purge_requests": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of purge requests"
+ }
+ },
+ "dbinfo": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "distribution of latencies for calls to retrieve DB info"
+ },
+ "io_queue": {
+ "search": {
+ "value": 1,
+ "type": "counter",
+ "desc": "Search IO directly triggered by client requests"
+ }
+ },
+ "io_queue2": {
+ "search": {
+ "count": {
+ "value": 1,
+ "type": "counter",
+ "desc": "Search IO directly triggered by client requests"
+ }
+ }
+ },
+ "auth_cache_hits": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of authentication cache hits"
+ },
+ "auth_cache_misses": {
+ "value": 2,
+ "type": "counter",
+ "desc": "number of authentication cache misses"
+ },
+ "collect_results_time": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "microsecond latency for calls to couch_db:collect_results/3"
+ },
+ "database_writes": {
+ "value": 14,
+ "type": "counter",
+ "desc": "number of times a database was changed"
+ },
+ "database_reads": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of times a document was read from a database"
+ },
+ "database_purges": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of times a database was purged"
+ },
+ "db_open_time": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "milliseconds required to open a database"
+ },
+ "document_inserts": {
+ "value": 17,
+ "type": "counter",
+ "desc": "number of documents inserted"
+ },
+ "document_writes": {
+ "value": 17,
+ "type": "counter",
+ "desc": "number of document write operations"
+ },
+ "document_purges": {
+ "total": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of total document purge operations"
+ },
+ "success": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of successful document purge operations"
+ },
+ "failure": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of failed document purge operations"
+ }
+ },
+ "local_document_writes": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of _local document write operations"
+ },
+ "httpd_request_methods": {
+ "COPY": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP COPY requests"
+ },
+ "DELETE": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP DELETE requests"
+ },
+ "GET": {
+ "value": 75544,
+ "type": "counter",
+ "desc": "number of HTTP GET requests"
+ },
+ "HEAD": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP HEAD requests"
+ },
+ "OPTIONS": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP OPTIONS requests"
+ },
+ "POST": {
+ "value": 15,
+ "type": "counter",
+ "desc": "number of HTTP POST requests"
+ },
+ "PUT": {
+ "value": 3,
+ "type": "counter",
+ "desc": "number of HTTP PUT requests"
+ }
+ },
+ "httpd_status_codes": {
+ "200": {
+ "value": 75294,
+ "type": "counter",
+ "desc": "number of HTTP 200 OK responses"
+ },
+ "201": {
+ "value": 15,
+ "type": "counter",
+ "desc": "number of HTTP 201 Created responses"
+ },
+ "202": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 202 Accepted responses"
+ },
+ "204": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 204 No Content responses"
+ },
+ "206": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 206 Partial Content"
+ },
+ "301": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 301 Moved Permanently responses"
+ },
+ "302": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 302 Found responses"
+ },
+ "304": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 304 Not Modified responses"
+ },
+ "400": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 400 Bad Request responses"
+ },
+ "401": {
+ "value": 20,
+ "type": "counter",
+ "desc": "number of HTTP 401 Unauthorized responses"
+ },
+ "403": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 403 Forbidden responses"
+ },
+ "404": {
+ "value": 225,
+ "type": "counter",
+ "desc": "number of HTTP 404 Not Found responses"
+ },
+ "405": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 405 Method Not Allowed responses"
+ },
+ "406": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 406 Not Acceptable responses"
+ },
+ "409": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 409 Conflict responses"
+ },
+ "412": {
+ "value": 3,
+ "type": "counter",
+ "desc": "number of HTTP 412 Precondition Failed responses"
+ },
+ "413": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 413 Request Entity Too Long responses"
+ },
+ "414": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 414 Request URI Too Long responses"
+ },
+ "415": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 415 Unsupported Media Type responses"
+ },
+ "416": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 416 Requested Range Not Satisfiable responses"
+ },
+ "417": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 417 Expectation Failed responses"
+ },
+ "500": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 500 Internal Server Error responses"
+ },
+ "501": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 501 Not Implemented responses"
+ },
+ "503": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP 503 Service unavailable responses"
+ }
+ },
+ "open_databases": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of open databases"
+ },
+ "open_os_files": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of file descriptors CouchDB has open"
+ },
+ "request_time": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of a request inside CouchDB without MochiWeb"
+ },
+ "couch_server": {
+ "lru_skip": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of couch_server LRU operations skipped"
+ }
+ },
+ "query_server": {
+ "vdu_rejects": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of rejections by validate_doc_update function"
+ },
+ "vdu_process_time": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "duration of validate_doc_update function calls"
+ }
+ },
+ "mrview": {
+ "map_doc": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of documents mapped in the view server"
+ },
+ "emits": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of invocations of `emit' in map functions in the view server"
+ }
+ }
+ },
+ "mem3": {
+ "shard_cache": {
+ "eviction": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of shard cache evictions"
+ },
+ "hit": {
+ "value": 185,
+ "type": "counter",
+ "desc": "number of shard cache hits"
+ },
+ "miss": {
+ "value": 252470,
+ "type": "counter",
+ "desc": "number of shard cache misses"
+ }
+ }
+ },
+ "ddoc_cache": {
+ "hit": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of design doc cache hits"
+ },
+ "miss": {
+ "value": 3,
+ "type": "counter",
+ "desc": "number of design doc cache misses"
+ },
+ "recovery": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of design doc cache recoveries"
+ }
+ },
+ "couch_log": {
+ "level": {
+ "alert": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of logged alert messages"
+ },
+ "critical": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of logged critical messages"
+ },
+ "debug": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of logged debug messages"
+ },
+ "emergency": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of logged emergency messages"
+ },
+ "error": {
+ "value": 2,
+ "type": "counter",
+ "desc": "number of logged error messages"
+ },
+ "info": {
+ "value": 8,
+ "type": "counter",
+ "desc": "number of logged info messages"
+ },
+ "notice": {
+ "value": 126250,
+ "type": "counter",
+ "desc": "number of logged notice messages"
+ },
+ "warning": {
+ "value": 8,
+ "type": "counter",
+ "desc": "number of logged warning messages"
+ }
+ }
+ },
+ "dreyfus": {
+ "httpd": {
+ "search": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "Distribution of overall search request latency as experienced by the end user"
+ }
+ },
+ "rpc": {
+ "search": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of a search RPC worker"
+ },
+ "group1": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of a group1 RPC worker"
+ },
+ "group2": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of a group2 RPC worker"
+ },
+ "info": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of an info RPC worker"
+ }
+ },
+ "index": {
+ "await": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of an dreyfus_index await request"
+ },
+ "search": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of an dreyfus_index search request"
+ },
+ "group1": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of an dreyfus_index group1 request"
+ },
+ "group2": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of an dreyfus_index group2 request"
+ },
+ "info": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of an dreyfus_index info request"
+ }
+ }
+ },
+ "fabric": {
+ "worker": {
+ "timeouts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of worker timeouts"
+ }
+ },
+ "open_shard": {
+ "timeouts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of open shard timeouts"
+ }
+ },
+ "read_repairs": {
+ "success": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of successful read repair operations"
+ },
+ "failure": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of failed read repair operations"
+ }
+ },
+ "doc_update": {
+ "errors": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of document update errors"
+ },
+ "mismatched_errors": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of document update errors with multiple error types"
+ },
+ "write_quorum_errors": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of write quorum errors"
+ }
+ }
+ },
+ "rexi": {
+ "buffered": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of rexi messages buffered"
+ },
+ "down": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of rexi_DOWN messages handled"
+ },
+ "dropped": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of rexi messages dropped from buffers"
+ },
+ "streams": {
+ "timeout": {
+ "init_stream": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of rexi stream initialization timeouts"
+ },
+ "stream": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of rexi stream timeouts"
+ },
+ "wait_for_ack": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of rexi stream timeouts while waiting for acks"
+ }
+ }
+ }
+ },
+ "couch_replicator": {
+ "changes_read_failures": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of failed replicator changes read failures"
+ },
+ "changes_reader_deaths": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of failed replicator changes readers"
+ },
+ "changes_manager_deaths": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of failed replicator changes managers"
+ },
+ "changes_queue_deaths": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of failed replicator changes work queues"
+ },
+ "checkpoints": {
+ "success": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of checkpoints successfully saves"
+ },
+ "failure": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of failed checkpoint saves"
+ }
+ },
+ "failed_starts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of replications that have failed to start"
+ },
+ "requests": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of HTTP requests made by the replicator"
+ },
+ "responses": {
+ "failure": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of failed HTTP responses received by the replicator"
+ },
+ "success": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of successful HTTP responses received by the replicator"
+ }
+ },
+ "stream_responses": {
+ "failure": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of failed streaming HTTP responses received by the replicator"
+ },
+ "success": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of successful streaming HTTP responses received by the replicator"
+ }
+ },
+ "worker_deaths": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of failed replicator workers"
+ },
+ "workers_started": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of replicator workers started"
+ },
+ "cluster_is_stable": {
+ "value": 1,
+ "type": "gauge",
+ "desc": "1 if cluster is stable, 0 if unstable"
+ },
+ "db_scans": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of times replicator db scans have been started"
+ },
+ "docs": {
+ "dbs_created": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of db shard creations seen by replicator doc processor"
+ },
+ "dbs_deleted": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of db shard deletions seen by replicator doc processor"
+ },
+ "dbs_found": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of db shard found by replicator doc processor"
+ },
+ "db_changes": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of db changes processed by replicator doc processor"
+ },
+ "failed_state_updates": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of 'failed' state document updates"
+ },
+ "completed_state_updates": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of 'completed' state document updates"
+ }
+ },
+ "jobs": {
+ "adds": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of jobs added to replicator scheduler"
+ },
+ "duplicate_adds": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of duplicate jobs added to replicator scheduler"
+ },
+ "removes": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of jobs removed from replicator scheduler"
+ },
+ "starts": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of jobs started by replicator scheduler"
+ },
+ "stops": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of jobs stopped by replicator scheduler"
+ },
+ "crashes": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of job crashed noticed by replicator scheduler"
+ },
+ "running": {
+ "value": 1,
+ "type": "gauge",
+ "desc": "replicator scheduler running jobs"
+ },
+ "pending": {
+ "value": 1,
+ "type": "gauge",
+ "desc": "replicator scheduler pending jobs"
+ },
+ "crashed": {
+ "value": 1,
+ "type": "gauge",
+ "desc": "replicator scheduler crashed jobs"
+ },
+ "total": {
+ "value": 1,
+ "type": "gauge",
+ "desc": "total number of replicator scheduler jobs"
+ }
+ },
+ "connection": {
+ "acquires": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of times connections are shared"
+ },
+ "creates": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of connections created"
+ },
+ "releases": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of times ownership of a connection is released"
+ },
+ "owner_crashes": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of times a connection owner crashes while owning at least one connection"
+ },
+ "worker_crashes": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of times a worker unexpectedly terminates"
+ },
+ "closes": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of times a worker is gracefully shut down"
+ }
+ }
+ },
+ "pread": {
+ "exceed_eof": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of the attempts to read beyond end of db file"
+ },
+ "exceed_limit": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of the attempts to read beyond set limit"
+ }
+ },
+ "mango": {
+ "unindexed_queries": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of mango queries that could not use an index"
+ },
+ "query_invalid_index": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of mango queries that generated an invalid index warning"
+ },
+ "too_many_docs_scanned": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of mango queries that generated an index scan warning"
+ },
+ "docs_examined": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of documents examined by mango queries coordinated by this node"
+ },
+ "quorum_docs_examined": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of documents examined by mango queries, using cluster quorum"
+ },
+ "results_returned": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of rows returned by mango queries"
+ },
+ "query_time": {
+ "value": {
+ "min": 0.0,
+ "max": 0.0,
+ "arithmetic_mean": 0.0,
+ "geometric_mean": 0.0,
+ "harmonic_mean": 0.0,
+ "median": 0.0,
+ "variance": 0.0,
+ "standard_deviation": 0.0,
+ "skewness": 0.0,
+ "kurtosis": 0.0,
+ "percentile": [
+ [
+ 50,
+ 0.0
+ ],
+ [
+ 75,
+ 0.0
+ ],
+ [
+ 90,
+ 0.0
+ ],
+ [
+ 95,
+ 0.0
+ ],
+ [
+ 99,
+ 0.0
+ ],
+ [
+ 999,
+ 0.0
+ ]
+ ],
+ "histogram": [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "n": 0
+ },
+ "type": "histogram",
+ "desc": "length of time processing a mango query"
+ },
+ "evaluate_selector": {
+ "value": 1,
+ "type": "counter",
+ "desc": "number of mango selector evaluations"
+ }
+ }
+}
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_system.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_system.json
new file mode 100644
index 000000000..7084645a4
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_system.json
@@ -0,0 +1,176 @@
+{
+ "uptime": 253571,
+ "memory": {
+ "other": 20427855,
+ "atom": 504433,
+ "atom_used": 488328,
+ "processes": 9161448,
+ "processes_used": 9160864,
+ "binary": 297696,
+ "code": 11252688,
+ "ets": 1579120
+ },
+ "run_queue": 1,
+ "ets_table_count": 116,
+ "context_switches": 22614499,
+ "reductions": 43211228312,
+ "garbage_collection_count": 11416345,
+ "words_reclaimed": 20241272866,
+ "io_input": 49674812,
+ "io_output": 686400800,
+ "os_proc_count": 1,
+ "stale_proc_count": 1,
+ "process_count": 296,
+ "process_limit": 262144,
+ "message_queues": {
+ "couch_file": {
+ "count": 2,
+ "min": 1,
+ "max": 1,
+ "50": 1,
+ "90": 1,
+ "99": 1
+ },
+ "couch_db_updater": {
+ "count": 2,
+ "min": 1,
+ "max": 1,
+ "50": 1,
+ "90": 1,
+ "99": 1
+ },
+ "httpc_manager": 1,
+ "httpc_handler_sup": 1,
+ "ken_sup": 1,
+ "ken_server": 1,
+ "couch_replication": 1,
+ "standard_error_sup": 1,
+ "chttpd_auth_cache_lru": 1,
+ "couch_index_sup": 1,
+ "ioq_sup": 1,
+ "couch_index_server": 1,
+ "mem3_events": 1,
+ "jwtf_sup": 1,
+ "jwtf_keystore": 1,
+ "ioq": 1,
+ "couch_uuids": 1,
+ "ftp_sup": 1,
+ "ibrowse_sup": 1,
+ "couch_secondary_services": 1,
+ "couch_primary_services": 1,
+ "couch_task_status": 1,
+ "couch_sup": 1,
+ "global_changes_sup": 1,
+ "global_changes_server": 1,
+ "couch_server": 1,
+ "couch_epi_functions_gen_couch_index": 1,
+ "couch_plugin": 1,
+ "ibrowse": 1,
+ "config_event": 1,
+ "couch_epi_functions_gen_chttpd_auth": 1,
+ "chttpd_sup": 1,
+ "couch_epi_functions_gen_couch_db": 1,
+ "couch_epi_data_gen_flags_config": 1,
+ "couch_epi_functions_gen_global_changes": 1,
+ "couch_proc_manager": 1,
+ "release_handler": 1,
+ "sasl_sup": 1,
+ "couch_epi_functions_gen_chttpd_handlers": 1,
+ "couch_epi_functions_gen_feature_flags": 1,
+ "couch_epi_functions_gen_chttpd": 1,
+ "dreyfus_sup": 1,
+ "sasl_safe_sup": 1,
+ "couch_event_sup2": 1,
+ "alarm_handler": 1,
+ "couch_event_server": 1,
+ "dreyfus_index_manager": 1,
+ "timer_server": 1,
+ "runtime_tools_sup": 1,
+ "couch_httpd_vhost": 1,
+ "chttpd_auth_cache": 1,
+ "couch_stats_sup": 1,
+ "couch_stats_process_tracker": 1,
+ "chttpd": 1,
+ "kernel_safe_sup": 1,
+ "tftp_sup": 1,
+ "couch_stats_aggregator": 1,
+ "rex": 1,
+ "folsom_sup": 1,
+ "inet_gethost_native_sup": 1,
+ "kernel_sup": 1,
+ "ddoc_cache_sup": 1,
+ "global_name_server": 1,
+ "ddoc_cache_opener": 1,
+ "folsom_sample_slide_sup": 1,
+ "ddoc_cache_lru": 1,
+ "file_server_2": 1,
+ "standard_error": 1,
+ "rexi_buffer_nonode@nohost": 1,
+ "rexi_server_nonode@nohost": 1,
+ "couch_drv": 1,
+ "couch_peruser_sup": 1,
+ "tls_connection_sup": 1,
+ "couch_peruser": 1,
+ "folsom_metrics_histogram_ets": 1,
+ "couch_replicator_sup": 1,
+ "ssl_sup": 1,
+ "couch_replicator_scheduler_sup": 1,
+ "smoosh_sup": 1,
+ "folsom_meter_timer_server": 1,
+ "smoosh_server": 1,
+ "couch_replicator_scheduler": 1,
+ "couch_epi_data_gen_dreyfus_black_list": 1,
+ "mem3_sync_nodes": 1,
+ "couch_replicator_rate_limiter": 1,
+ "inet_gethost_native": 1,
+ "inets_sup": 1,
+ "setup_sup": 1,
+ "inet_db": 1,
+ "ssl_pem_cache": 1,
+ "mem3_sync": 1,
+ "ssl_manager": 1,
+ "mem3_sup": 1,
+ "ssl_listen_tracker_sup": 1,
+ "mem3_shards": 1,
+ "mem3_seeds": 1,
+ "httpd_sup": 1,
+ "couch_log_sup": 1,
+ "mem3_reshard_sup": 1,
+ "mango_sup": 1,
+ "couch_log_server": 1,
+ "mem3_reshard_job_sup": 1,
+ "erts_code_purger": 1,
+ "global_group": 1,
+ "error_logger": 1,
+ "couch_replicator_doc_processor": 1,
+ "ssl_connection_sup": 1,
+ "init": 1,
+ "mem3_reshard_dbdoc": 1,
+ "couch_replicator_connection": 1,
+ "erl_signal_server": 1,
+ "couch_replicator_clustering": 1,
+ "config": 1,
+ "mem3_reshard": 1,
+ "user": 1,
+ "couch_epi_sup": 1,
+ "mem3_nodes": 1,
+ "ssl_admin_sup": 1,
+ "mochiweb_clock": 1,
+ "rexi_buffer_mon": 1,
+ "dtls_udp_sup": 1,
+ "rexi_buffer_sup": 1,
+ "erl_prim_loader": 1,
+ "code_server": 1,
+ "httpc_sup": 1,
+ "rexi_sup": 1,
+ "dtls_connection_sup": 1,
+ "rexi_server_sup": 1,
+ "rexi_server_mon": 1,
+ "application_controller": 1,
+ "httpc_profile_sup": 1,
+ "config_sup": 1,
+ "rexi_server": 1
+ },
+ "internal_replication_jobs": 1,
+ "distribution": {}
+}
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/root.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/root.json
new file mode 100644
index 000000000..e7feb41c7
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/root.json
@@ -0,0 +1,16 @@
+{
+ "couchdb": "Welcome",
+ "version": "3.1.1",
+ "git_sha": "ce596c65d",
+ "uuid": "d7bc2230b8e4de7f20680091bd7a21c7",
+ "features": [
+ "access-ready",
+ "partitioned",
+ "pluggable-storage-engines",
+ "reshard",
+ "scheduler"
+ ],
+ "vendor": {
+ "name": "The Apache Software Foundation"
+ }
+}