@cryptotaxi247 / netdata-1 / commits / bacd83a36

go.d dnsmasq: query metrics individually to handle v2.90+ SERVFAIL (#18376)

Ilya Mashchenko committed Aug 19, 2024 at 18:28 UTC bacd83a36570e9f8a2a99c8ba72fb67e7d0b3d5f
1 file changed +65 -52
src/go/plugin/go.d/modules/dnsmasq/collect.go
+65 -52
@@ -11,20 +11,16 @@ import (
11 )
12
13 func (d *Dnsmasq) collect() (map[string]int64, error) {
14 - r, err := d.queryCacheStatistics()
15 - if err != nil {
16 - return nil, err
17 - }
14 + mx := make(map[string]int64)
15
19 - ms := make(map[string]int64)
20 - if err = d.collectResponse(ms, r); err != nil {
16 + if err := d.collectCacheStatistics(mx); err != nil {
17 return nil, err
18 }
19
24 - return ms, nil
20 + return mx, nil
21 }
22
27 -func (d *Dnsmasq) collectResponse(ms map[string]int64, resp *dns.Msg) error {
23 +func (d *Dnsmasq) collectCacheStatistics(mx map[string]int64) error {
24 /*
25 ;; flags: qr aa rd ra; QUERY: 7, ANSWER: 7, AUTHORITY: 0, ADDITIONAL: 0
26
@@ -46,68 +42,82 @@ func (d *Dnsmasq) collectResponse(ms map[string]int64, resp *dns.Msg) error {
42 auth.bind. 0 CH TXT "0"
43 servers.bind. 0 CH TXT "10.0.0.1#53 0 0" "1.1.1.1#53 4 3" "1.0.0.1#53 3 0"
44 */
49 - for _, a := range resp.Answer {
50 - txt, ok := a.(*dns.TXT)
51 - if !ok {
52 - continue
53 - }
45
55 - idx := strings.IndexByte(txt.Hdr.Name, '.')
56 - if idx == -1 {
57 - continue
46 + questions := []string{
47 + "servers.bind.",
48 + "cachesize.bind.",
49 + "insertions.bind.",
50 + "evictions.bind.",
51 + "hits.bind.",
52 + "misses.bind.",
53 + // auth.bind query is only supported if dnsmasq has been built to support running as an authoritative name server
54 + // See https://github.com/netdata/netdata/issues/13766
55 + //"auth.bind.",
56 + }
57 +
58 + for _, q := range questions {
59 + resp, err := d.query(q)
60 + if err != nil {
61 + return err
62 }
63
60 - switch name := txt.Hdr.Name[:idx]; name {
61 - case "servers":
62 - for _, entry := range txt.Txt {
63 - parts := strings.Fields(entry)
64 - if len(parts) != 3 {
65 - return fmt.Errorf("parse %s (%s): unexpected format", txt.Hdr.Name, entry)
64 + for _, a := range resp.Answer {
65 + txt, ok := a.(*dns.TXT)
66 + if !ok {
67 + continue
68 + }
69 +
70 + idx := strings.IndexByte(txt.Hdr.Name, '.')
71 + if idx == -1 {
72 + continue
73 + }
74 +
75 + name := txt.Hdr.Name[:idx]
76 +
77 + switch name {
78 + case "servers":
79 + for _, entry := range txt.Txt {
80 + parts := strings.Fields(entry)
81 + if len(parts) != 3 {
82 + return fmt.Errorf("parse %s (%s): unexpected format", txt.Hdr.Name, entry)
83 + }
84 + queries, err := strconv.ParseFloat(parts[1], 64)
85 + if err != nil {
86 + return fmt.Errorf("parse '%s' (%s): %v", txt.Hdr.Name, entry, err)
87 + }
88 + failedQueries, err := strconv.ParseFloat(parts[2], 64)
89 + if err != nil {
90 + return fmt.Errorf("parse '%s' (%s): %v", txt.Hdr.Name, entry, err)
91 + }
92 +
93 + mx["queries"] += int64(queries)
94 + mx["failed_queries"] += int64(failedQueries)
95 }
67 - queries, err := strconv.ParseFloat(parts[1], 64)
68 - if err != nil {
69 - return fmt.Errorf("parse '%s' (%s): %v", txt.Hdr.Name, entry, err)
96 + case "cachesize", "insertions", "evictions", "hits", "misses", "auth":
97 + if len(txt.Txt) != 1 {
98 + return fmt.Errorf("parse '%s' (%v): unexpected format", txt.Hdr.Name, txt.Txt)
99 }
71 - failedQueries, err := strconv.ParseFloat(parts[2], 64)
100 + v, err := strconv.ParseFloat(txt.Txt[0], 64)
101 if err != nil {
73 - return fmt.Errorf("parse '%s' (%s): %v", txt.Hdr.Name, entry, err)
102 + return fmt.Errorf("parse '%s' (%s): %v", txt.Hdr.Name, txt.Txt[0], err)
103 }
104
76 - ms["queries"] += int64(queries)
77 - ms["failed_queries"] += int64(failedQueries)
78 - }
79 - case "cachesize", "insertions", "evictions", "hits", "misses", "auth":
80 - if len(txt.Txt) != 1 {
81 - return fmt.Errorf("parse '%s' (%v): unexpected format", txt.Hdr.Name, txt.Txt)
82 - }
83 - v, err := strconv.ParseFloat(txt.Txt[0], 64)
84 - if err != nil {
85 - return fmt.Errorf("parse '%s' (%s): %v", txt.Hdr.Name, txt.Txt[0], err)
105 + mx[name] = int64(v)
106 }
87 -
88 - ms[name] = int64(v)
107 }
108 }
109 +
110 return nil
111 }
112
94 -func (d *Dnsmasq) queryCacheStatistics() (*dns.Msg, error) {
113 +func (d *Dnsmasq) query(question string) (*dns.Msg, error) {
114 msg := &dns.Msg{
115 MsgHdr: dns.MsgHdr{
116 Id: dns.Id(),
117 RecursionDesired: true,
118 },
119 Question: []dns.Question{
101 - {Name: "cachesize.bind.", Qtype: dns.TypeTXT, Qclass: dns.ClassCHAOS},
102 - {Name: "insertions.bind.", Qtype: dns.TypeTXT, Qclass: dns.ClassCHAOS},
103 - {Name: "evictions.bind.", Qtype: dns.TypeTXT, Qclass: dns.ClassCHAOS},
104 - {Name: "hits.bind.", Qtype: dns.TypeTXT, Qclass: dns.ClassCHAOS},
105 - {Name: "misses.bind.", Qtype: dns.TypeTXT, Qclass: dns.ClassCHAOS},
106 - // TODO: collect auth.bind if available
107 - // auth.bind query is only supported if dnsmasq has been built
108 - // to support running as an authoritative name server. See https://github.com/netdata/netdata/issues/13766
109 - //{Name: "auth.bind.", Qtype: dns.TypeTXT, Qclass: dns.ClassCHAOS},
110 - {Name: "servers.bind.", Qtype: dns.TypeTXT, Qclass: dns.ClassCHAOS},
120 + {Name: question, Qtype: dns.TypeTXT, Qclass: dns.ClassCHAOS},
121 },
122 }
123
@@ -115,12 +125,15 @@ func (d *Dnsmasq) queryCacheStatistics() (*dns.Msg, error) {
125 if err != nil {
126 return nil, err
127 }
128 +
129 if r == nil {
119 - return nil, fmt.Errorf("'%s' returned an empty response", d.Address)
130 + return nil, fmt.Errorf("'%s' question '%s', returned an empty response", d.Address, question)
131 }
132 +
133 if r.Rcode != dns.RcodeSuccess {
134 s := dns.RcodeToString[r.Rcode]
123 - return nil, fmt.Errorf("'%s' returned '%s' (%d) response code", d.Address, s, r.Rcode)
135 + return nil, fmt.Errorf("'%s' question '%s' returned '%s' (%d) response code", d.Address, question, s, r.Rcode)
136 }
137 +
138 return r, nil
139 }