diff --git a/monitoring/collectd/scripts/unbound/unbound.rb b/monitoring/collectd/scripts/unbound/unbound.rb new file mode 100644 index 0000000..cd1124d --- /dev/null +++ b/monitoring/collectd/scripts/unbound/unbound.rb @@ -0,0 +1,175 @@ +#!/usr/local/bin/ruby +$stdout.sync = true + +COLLECTD_INTERVAL = ENV['COLLECTD_INTERVAL'] ? ENV['COLLECTD_INTERVAL'].to_i : 10 +COLLECTD_HOSTNAME = ENV['COLLECTD_HOSTNAME'] || 'localhost' + +class Stats + def initialize(stats, interval) + @stats = stats + @interval = interval + end + + def histogram + @stats.select { |key, _| key.include? 'histogram' }.values.map(&:to_i) + end + + def histogram_percent + total_queries = histogram.reduce(&:+).to_f + histogram.map { |value| value * 100 / total_queries } + end + + def current_time + @stats['time.now'].split('.').first + end + + def putval(name, data) + "PUTVAL #{COLLECTD_HOSTNAME}/unbound/#{name} interval=#{@interval} #{current_time}:#{data.join(':')}" + end + + def thread_stats + @stats.select { |key, _| key.include? 'histogram' }.values.map(&:to_i) + end + + # msg.cache.count=1059 + # rrset.cache.count=913 + # infra.cache.count=7 + # key.cache.count=0 + def cache_counts + [@stats['msg.cache.count'], @stats['rrset.cache.count'], @stats['infra.cache.count'], @stats['key.cache.count']] + end + + # thread1.recursion.time.avg=0.079665 + # thread1.recursion.time.median=0.0541417 + def recursion_times + stats = @stats.select{ |key, _| key.include? 'recursion.time' } + result = stats.group_by { |key, value| key.split('.').first } + result.each { |key, value| result[key] = Hash[value] } + result.each { |key, value| result[key] = result[key].values } + result + end + + # thread0.requestlist.avg=1.07819 + # thread0.requestlist.max=8 + # thread0.requestlist.overwritten=0 + # thread0.requestlist.exceeded=0 + # thread0.requestlist.current.all=0 + # thread0.requestlist.current.user=0 + def request_list + stats = @stats.select{ |key, _| key.include? 'requestlist' } + result = stats.group_by { |key, value| key.split('.').first } + result.each { |key, value| result[key] = Hash[value] } + result.each { |key, value| result[key] = result[key].values } + result + end + + # thread0.num.queries=2903 + # thread0.num.cachehits=1445 + # thread0.num.cachemiss=1458 + # thread0.num.prefetch=0 + # thread0.num.recursivereplies=1458 + def requests + stats = @stats.select{ |key, _| key =~ /^(thread.*|total)\.num/ } + result = stats.group_by { |key, value| key.split('.').first } + result.each { |key, value| result[key] = Hash[value] } + result.each { |key, value| result[key] = result[key].values } + result + end + + # num.query.type.A=1347 + # num.query.type.PTR=1966 + # num.query.type.AAAA=889 + # num.query.type.SRV=5 + # num.query.class.IN=4207 + # num.query.opcode.QUERY=4207 + # num.query.tcp=0 + # num.query.tcpout=0 + # num.query.ipv6=1505 + # num.query.flags.QR=0 + # num.query.flags.AA=0 + # num.query.flags.TC=0 + # num.query.flags.RD=4207 + # num.query.flags.RA=0 + # num.query.flags.Z=0 + # num.query.flags.AD=0 + # num.query.flags.CD=0 + # num.query.edns.present=2126 + # num.query.edns.DO=19 + def queries + @stats.select { |key, _| key =~ /^num\.query/ }.values + end + + # num.answer.rcode.NOERROR=3674 + # num.answer.rcode.FORMERR=0 + # num.answer.rcode.SERVFAIL=40 + # num.answer.rcode.NXDOMAIN=493 + # num.answer.rcode.NOTIMPL=0 + # num.answer.rcode.REFUSED=0 + # num.answer.rcode.nodata=310 + # num.answer.secure=0 + # num.answer.bogus=0 + def answers + @stats.select { |key, _| key =~ /^num\.answer/ }.values + end + + # mem.total.sbrk=0 + # mem.cache.rrset=184663 + # mem.cache.message=168614 + # mem.mod.iterator=16472 + # mem.mod.validator=33156 + def memory + @stats.select { |key, _| key =~ /^mem/ } + end + + def to_putvals + result = "" + result += putval('histogram_percent', histogram_percent) + result += "\n" + result += putval('histogram', histogram) + result += "\n" + result += putval('cache_counts', cache_counts) + result += "\n" + + recursion_times.each do |key, value| + result += putval("recursion_times-#{key}", value) + result += "\n" + end + + request_list.each do |key, value| + result += putval("request_list-#{key}", value) + result += "\n" + end + + requests.each do |key, value| + result += putval("unbound_requests-#{key}", value) + result += "\n" + end + + result += putval('unbound_queries', queries) + result += "\n" + + result += putval('unbound_answers', answers) + result += "\n" + + memory.each do |key, value| + result += putval("memory-#{key.gsub('.', '_')}", [value]) + result += "\n" + end + + result + end +end + +# time.up=5571.770754 +# time.elapsed=5571.770754 +# num.rrset.bogus=0 +# unwanted.queries=0 +# unwanted.replies=0 + +loop do + stats = Stats.new(Hash[`/usr/local/sbin/unbound-control stats` + .split("\n").map { |row| row.split '=' }], COLLECTD_INTERVAL) + puts stats.to_putvals + + sleep COLLECTD_INTERVAL +end