summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/unbound/charts.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/go/collectors/go.d.plugin/modules/unbound/charts.go527
1 files changed, 527 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/unbound/charts.go b/src/go/collectors/go.d.plugin/modules/unbound/charts.go
new file mode 100644
index 000000000..0f0607664
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/unbound/charts.go
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package unbound
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+
+ "golang.org/x/text/cases"
+ "golang.org/x/text/language"
+)
+
+type (
+ // Charts is an alias for module.Charts
+ Charts = module.Charts
+ // Chart is an alias for module.Charts
+ Chart = module.Chart
+ // Dims is an alias for module.Dims
+ Dims = module.Dims
+ // Dim is an alias for module.Dim
+ Dim = module.Dim
+)
+
+const (
+ prioQueries = module.Priority + iota
+ prioIPRateLimitedQueries
+ prioQueryType
+ prioQueryClass
+ prioQueryOpCode
+ prioQueryFlag
+ prioDNSCryptQueries
+
+ prioRecurReplies
+ prioReplyRCode
+
+ prioRecurTime
+
+ prioCache
+ prioCachePercentage
+ prioCachePrefetch
+ prioCacheExpired
+ prioZeroTTL
+ prioCacheCount
+
+ prioReqListUsage
+ prioReqListCurUsage
+ prioReqListJostle
+
+ prioTCPUsage
+
+ prioMemCache
+ prioMemMod
+ prioMemStreamWait
+ prioUptime
+
+ prioThread
+)
+
+func charts(cumulative bool) *Charts {
+ return &Charts{
+ makeIncrIf(queriesChart.Copy(), cumulative),
+ makeIncrIf(ipRateLimitedQueriesChart.Copy(), cumulative),
+ makeIncrIf(cacheChart.Copy(), cumulative),
+ makePercOfIncrIf(cachePercentageChart.Copy(), cumulative),
+ makeIncrIf(prefetchChart.Copy(), cumulative),
+ makeIncrIf(expiredChart.Copy(), cumulative),
+ makeIncrIf(zeroTTLChart.Copy(), cumulative),
+ makeIncrIf(dnsCryptChart.Copy(), cumulative),
+ makeIncrIf(recurRepliesChart.Copy(), cumulative),
+ recurTimeChart.Copy(),
+ reqListUsageChart.Copy(),
+ reqListCurUsageChart.Copy(),
+ makeIncrIf(reqListJostleChart.Copy(), cumulative),
+ tcpUsageChart.Copy(),
+ uptimeChart.Copy(),
+ }
+}
+
+func extendedCharts(cumulative bool) *Charts {
+ return &Charts{
+ memCacheChart.Copy(),
+ memModChart.Copy(),
+ memStreamWaitChart.Copy(),
+ cacheCountChart.Copy(),
+ makeIncrIf(queryTypeChart.Copy(), cumulative),
+ makeIncrIf(queryClassChart.Copy(), cumulative),
+ makeIncrIf(queryOpCodeChart.Copy(), cumulative),
+ makeIncrIf(queryFlagChart.Copy(), cumulative),
+ makeIncrIf(answerRCodeChart.Copy(), cumulative),
+ }
+}
+
+func threadCharts(thread string, cumulative bool) *Charts {
+ charts := charts(cumulative)
+ _ = charts.Remove(uptimeChart.ID)
+
+ for i, chart := range *charts {
+ convertTotalChartToThread(chart, thread, prioThread+i)
+ }
+ return charts
+}
+
+func convertTotalChartToThread(chart *Chart, thread string, priority int) {
+ chart.ID = fmt.Sprintf("%s_%s", thread, chart.ID)
+ chart.Title = fmt.Sprintf("%s %s",
+ cases.Title(language.English, cases.Compact).String(thread),
+ chart.Title,
+ )
+ chart.Fam = thread + "_stats"
+ chart.Ctx = "thread_" + chart.Ctx
+ chart.Priority = priority
+ for _, dim := range chart.Dims {
+ dim.ID = strings.Replace(dim.ID, "total", thread, 1)
+ }
+}
+
+// Common stats charts
+// see https://nlnetlabs.nl/documentation/unbound/unbound-control for the stats provided by unbound-control
+var (
+ queriesChart = Chart{
+ ID: "queries",
+ Title: "Received Queries",
+ Units: "queries",
+ Fam: "queries",
+ Ctx: "unbound.queries",
+ Priority: prioQueries,
+ Dims: Dims{
+ {ID: "total.num.queries", Name: "queries"},
+ },
+ }
+ ipRateLimitedQueriesChart = Chart{
+ ID: "queries_ip_ratelimited",
+ Title: "Rate Limited Queries",
+ Units: "queries",
+ Fam: "queries",
+ Ctx: "unbound.queries_ip_ratelimited",
+ Priority: prioIPRateLimitedQueries,
+ Dims: Dims{
+ {ID: "total.num.queries_ip_ratelimited", Name: "ratelimited"},
+ },
+ }
+ // ifdef USE_DNSCRYPT
+ dnsCryptChart = Chart{
+ ID: "dnscrypt_queries",
+ Title: "DNSCrypt Queries",
+ Units: "queries",
+ Fam: "queries",
+ Ctx: "unbound.dnscrypt_queries",
+ Priority: prioDNSCryptQueries,
+ Dims: Dims{
+ {ID: "total.num.dnscrypt.crypted", Name: "crypted"},
+ {ID: "total.num.dnscrypt.cert", Name: "cert"},
+ {ID: "total.num.dnscrypt.cleartext", Name: "cleartext"},
+ {ID: "total.num.dnscrypt.malformed", Name: "malformed"},
+ },
+ }
+ cacheChart = Chart{
+ ID: "cache",
+ Title: "Cache Statistics",
+ Units: "events",
+ Fam: "cache",
+ Ctx: "unbound.cache",
+ Type: module.Stacked,
+ Priority: prioCache,
+ Dims: Dims{
+ {ID: "total.num.cachehits", Name: "hits"},
+ {ID: "total.num.cachemiss", Name: "miss"},
+ },
+ }
+ cachePercentageChart = Chart{
+ ID: "cache_percentage",
+ Title: "Cache Statistics Percentage",
+ Units: "percentage",
+ Fam: "cache",
+ Ctx: "unbound.cache_percentage",
+ Type: module.Stacked,
+ Priority: prioCachePercentage,
+ Dims: Dims{
+ {ID: "total.num.cachehits", Name: "hits", Algo: module.PercentOfAbsolute},
+ {ID: "total.num.cachemiss", Name: "miss", Algo: module.PercentOfAbsolute},
+ },
+ }
+ prefetchChart = Chart{
+ ID: "cache_prefetch",
+ Title: "Cache Prefetches",
+ Units: "prefetches",
+ Fam: "cache",
+ Ctx: "unbound.prefetch",
+ Priority: prioCachePrefetch,
+ Dims: Dims{
+ {ID: "total.num.prefetch", Name: "prefetches"},
+ },
+ }
+ expiredChart = Chart{
+ ID: "cache_expired",
+ Title: "Replies Served From Expired Cache",
+ Units: "replies",
+ Fam: "cache",
+ Ctx: "unbound.expired",
+ Priority: prioCacheExpired,
+ Dims: Dims{
+ {ID: "total.num.expired", Name: "expired"},
+ },
+ }
+ zeroTTLChart = Chart{
+ ID: "zero_ttl_replies",
+ Title: "Replies Served From Expired Cache",
+ Units: "replies",
+ Fam: "cache",
+ Ctx: "unbound.zero_ttl_replies",
+ Priority: prioZeroTTL,
+ Dims: Dims{
+ {ID: "total.num.zero_ttl", Name: "zero_ttl"},
+ },
+ }
+ recurRepliesChart = Chart{
+ ID: "recursive_replies",
+ Title: "Replies That Needed Recursive Processing",
+ Units: "replies",
+ Fam: "replies",
+ Ctx: "unbound.recursive_replies",
+ Priority: prioRecurReplies,
+ Dims: Dims{
+ {ID: "total.num.recursivereplies", Name: "recursive"},
+ },
+ }
+ recurTimeChart = Chart{
+ ID: "recursion_time",
+ Title: "Time Spent On Recursive Processing",
+ Units: "milliseconds",
+ Fam: "recursion timings",
+ Ctx: "unbound.recursion_time",
+ Priority: prioRecurTime,
+ Dims: Dims{
+ {ID: "total.recursion.time.avg", Name: "avg"},
+ {ID: "total.recursion.time.median", Name: "median"},
+ },
+ }
+ reqListUsageChart = Chart{
+ ID: "request_list_usage",
+ Title: "Request List Usage",
+ Units: "queries",
+ Fam: "request list",
+ Ctx: "unbound.request_list_usage",
+ Priority: prioReqListUsage,
+ Dims: Dims{
+ {ID: "total.requestlist.avg", Name: "avg", Div: 1000},
+ {ID: "total.requestlist.max", Name: "max"}, // all time max in cumulative mode, never resets
+ },
+ }
+ reqListCurUsageChart = Chart{
+ ID: "current_request_list_usage",
+ Title: "Current Request List Usage",
+ Units: "queries",
+ Fam: "request list",
+ Ctx: "unbound.current_request_list_usage",
+ Type: module.Area,
+ Priority: prioReqListCurUsage,
+ Dims: Dims{
+ {ID: "total.requestlist.current.all", Name: "all"},
+ {ID: "total.requestlist.current.user", Name: "user"},
+ },
+ }
+ reqListJostleChart = Chart{
+ ID: "request_list_jostle_list",
+ Title: "Request List Jostle List Events",
+ Units: "queries",
+ Fam: "request list",
+ Ctx: "unbound.request_list_jostle_list",
+ Priority: prioReqListJostle,
+ Dims: Dims{
+ {ID: "total.requestlist.overwritten", Name: "overwritten"},
+ {ID: "total.requestlist.exceeded", Name: "dropped"},
+ },
+ }
+ tcpUsageChart = Chart{
+ ID: "tcpusage",
+ Title: "TCP Handler Buffers",
+ Units: "buffers",
+ Fam: "tcp buffers",
+ Ctx: "unbound.tcpusage",
+ Priority: prioTCPUsage,
+ Dims: Dims{
+ {ID: "total.tcpusage", Name: "usage"},
+ },
+ }
+ uptimeChart = Chart{
+ ID: "uptime",
+ Title: "Uptime",
+ Units: "seconds",
+ Fam: "uptime",
+ Ctx: "unbound.uptime",
+ Priority: prioUptime,
+ Dims: Dims{
+ {ID: "time.up", Name: "time"},
+ },
+ }
+)
+
+// Extended stats charts
+var (
+ // TODO: do not add dnscrypt stuff by default?
+ memCacheChart = Chart{
+ ID: "cache_memory",
+ Title: "Cache Memory",
+ Units: "KB",
+ Fam: "mem",
+ Ctx: "unbound.cache_memory",
+ Type: module.Stacked,
+ Priority: prioMemCache,
+ Dims: Dims{
+ {ID: "mem.cache.message", Name: "message", Div: 1024},
+ {ID: "mem.cache.rrset", Name: "rrset", Div: 1024},
+ {ID: "mem.cache.dnscrypt_nonce", Name: "dnscrypt_nonce", Div: 1024}, // ifdef USE_DNSCRYPT
+ {ID: "mem.cache.dnscrypt_shared_secret", Name: "dnscrypt_shared_secret", Div: 1024}, // ifdef USE_DNSCRYPT
+ },
+ }
+ // TODO: do not add subnet and ipsecmod stuff by default?
+ memModChart = Chart{
+ ID: "mod_memory",
+ Title: "Module Memory",
+ Units: "KB",
+ Fam: "mem",
+ Ctx: "unbound.mod_memory",
+ Type: module.Stacked,
+ Priority: prioMemMod,
+ Dims: Dims{
+ {ID: "mem.mod.iterator", Name: "iterator", Div: 1024},
+ {ID: "mem.mod.respip", Name: "respip", Div: 1024},
+ {ID: "mem.mod.validator", Name: "validator", Div: 1024},
+ {ID: "mem.mod.subnet", Name: "subnet", Div: 1024}, // ifdef CLIENT_SUBNET
+ {ID: "mem.mod.ipsecmod", Name: "ipsec", Div: 1024}, // ifdef USE_IPSECMOD
+ },
+ }
+ memStreamWaitChart = Chart{
+ ID: "mem_stream_wait",
+ Title: "TCP and TLS Stream Waif Buffer Memory",
+ Units: "KB",
+ Fam: "mem",
+ Ctx: "unbound.mem_streamwait",
+ Priority: prioMemStreamWait,
+ Dims: Dims{
+ {ID: "mem.streamwait", Name: "streamwait", Div: 1024},
+ },
+ }
+ // NOTE: same family as for cacheChart
+ cacheCountChart = Chart{
+ ID: "cache_count",
+ Title: "Cache Items Count",
+ Units: "items",
+ Fam: "cache",
+ Ctx: "unbound.cache_count",
+ Type: module.Stacked,
+ Priority: prioCacheCount,
+ Dims: Dims{
+ {ID: "infra.cache.count", Name: "infra"},
+ {ID: "key.cache.count", Name: "key"},
+ {ID: "msg.cache.count", Name: "msg"},
+ {ID: "rrset.cache.count", Name: "rrset"},
+ {ID: "dnscrypt_nonce.cache.count", Name: "dnscrypt_nonce"},
+ {ID: "dnscrypt_shared_secret.cache.count", Name: "shared_secret"},
+ },
+ }
+ queryTypeChart = Chart{
+ ID: "queries_by_type",
+ Title: "Queries By Type",
+ Units: "queries",
+ Fam: "queries",
+ Ctx: "unbound.type_queries",
+ Type: module.Stacked,
+ Priority: prioQueryType,
+ }
+ queryClassChart = Chart{
+ ID: "queries_by_class",
+ Title: "Queries By Class",
+ Units: "queries",
+ Fam: "queries",
+ Ctx: "unbound.class_queries",
+ Type: module.Stacked,
+ Priority: prioQueryClass,
+ }
+ queryOpCodeChart = Chart{
+ ID: "queries_by_opcode",
+ Title: "Queries By OpCode",
+ Units: "queries",
+ Fam: "queries",
+ Ctx: "unbound.opcode_queries",
+ Type: module.Stacked,
+ Priority: prioQueryOpCode,
+ }
+ queryFlagChart = Chart{
+ ID: "queries_by_flag",
+ Title: "Queries By Flag",
+ Units: "queries",
+ Fam: "queries",
+ Ctx: "unbound.flag_queries",
+ Type: module.Stacked,
+ Priority: prioQueryFlag,
+ Dims: Dims{
+ {ID: "num.query.flags.QR", Name: "QR"},
+ {ID: "num.query.flags.AA", Name: "AA"},
+ {ID: "num.query.flags.TC", Name: "TC"},
+ {ID: "num.query.flags.RD", Name: "RD"},
+ {ID: "num.query.flags.RA", Name: "RA"},
+ {ID: "num.query.flags.Z", Name: "Z"},
+ {ID: "num.query.flags.AD", Name: "AD"},
+ {ID: "num.query.flags.CD", Name: "CD"},
+ },
+ }
+ answerRCodeChart = Chart{
+ ID: "replies_by_rcode",
+ Title: "Replies By RCode",
+ Units: "replies",
+ Fam: "replies",
+ Ctx: "unbound.rcode_answers",
+ Type: module.Stacked,
+ Priority: prioReplyRCode,
+ }
+)
+
+func (u *Unbound) updateCharts() {
+ if len(u.curCache.threads) > 1 {
+ for v := range u.curCache.threads {
+ if !u.cache.threads[v] {
+ u.cache.threads[v] = true
+ u.addThreadCharts(v)
+ }
+ }
+ }
+ // 0-6 rcodes always included
+ if hasExtendedData := len(u.curCache.answerRCode) > 0; !hasExtendedData {
+ return
+ }
+
+ if !u.extChartsCreated {
+ charts := extendedCharts(u.Cumulative)
+ if err := u.Charts().Add(*charts...); err != nil {
+ u.Warningf("add extended charts: %v", err)
+ }
+ u.extChartsCreated = true
+ }
+
+ for v := range u.curCache.queryType {
+ if !u.cache.queryType[v] {
+ u.cache.queryType[v] = true
+ u.addDimToQueryTypeChart(v)
+ }
+ }
+ for v := range u.curCache.queryClass {
+ if !u.cache.queryClass[v] {
+ u.cache.queryClass[v] = true
+ u.addDimToQueryClassChart(v)
+ }
+ }
+ for v := range u.curCache.queryOpCode {
+ if !u.cache.queryOpCode[v] {
+ u.cache.queryOpCode[v] = true
+ u.addDimToQueryOpCodeChart(v)
+ }
+ }
+ for v := range u.curCache.answerRCode {
+ if !u.cache.answerRCode[v] {
+ u.cache.answerRCode[v] = true
+ u.addDimToAnswerRcodeChart(v)
+ }
+ }
+}
+
+func (u *Unbound) addThreadCharts(thread string) {
+ charts := threadCharts(thread, u.Cumulative)
+ if err := u.Charts().Add(*charts...); err != nil {
+ u.Warningf("add '%s' charts: %v", thread, err)
+ }
+}
+
+func (u *Unbound) addDimToQueryTypeChart(typ string) {
+ u.addDimToChart(queryTypeChart.ID, "num.query.type."+typ, typ)
+}
+func (u *Unbound) addDimToQueryClassChart(class string) {
+ u.addDimToChart(queryClassChart.ID, "num.query.class."+class, class)
+}
+func (u *Unbound) addDimToQueryOpCodeChart(opcode string) {
+ u.addDimToChart(queryOpCodeChart.ID, "num.query.opcode."+opcode, opcode)
+}
+func (u *Unbound) addDimToAnswerRcodeChart(rcode string) {
+ u.addDimToChart(answerRCodeChart.ID, "num.answer.rcode."+rcode, rcode)
+}
+
+func (u *Unbound) addDimToChart(chartID, dimID, dimName string) {
+ chart := u.Charts().Get(chartID)
+ if chart == nil {
+ u.Warningf("add '%s' dim: couldn't find '%s' chart", dimID, chartID)
+ return
+ }
+ dim := &Dim{ID: dimID, Name: dimName}
+ if u.Cumulative {
+ dim.Algo = module.Incremental
+ }
+ if err := chart.AddDim(dim); err != nil {
+ u.Warningf("add '%s' dim: %v", dimID, err)
+ return
+ }
+ chart.MarkNotCreated()
+}
+
+func makeIncrIf(chart *Chart, do bool) *Chart {
+ if !do {
+ return chart
+ }
+ chart.Units += "/s"
+ for _, d := range chart.Dims {
+ d.Algo = module.Incremental
+ }
+ return chart
+}
+
+func makePercOfIncrIf(chart *Chart, do bool) *Chart {
+ if !do {
+ return chart
+ }
+ for _, d := range chart.Dims {
+ d.Algo = module.PercentOfIncremental
+ }
+ return chart
+}