bin/check-dns.rb in sensu-plugins-dns-1.1.0 vs bin/check-dns.rb in sensu-plugins-dns-1.2.0

- old
+ new

@@ -95,10 +95,24 @@ description: 'Use tcp for resolution', short: '-T', long: '--use-tcp', boolean: true + option :request_count, + description: 'Number of DNS requests to send', + short: '-c COUNT', + long: '--request_count COUNT', + proc: proc(&:to_i), + default: 1 + + option :threshold, + description: 'Percentage of DNS queries that must succeed', + short: '-l PERCENT', + long: '--threshold PERCENT', + proc: proc(&:to_i), + default: 100 + option :timeout, description: 'Set timeout for query', short: '-T TIMEOUT', long: '--timeout TIMEOUT', proc: proc(&:to_i), @@ -109,14 +123,25 @@ dnsruby_config[:nameserver] = [config[:server]] unless config[:server].nil? dnsruby_config[:port] = config[:port] unless config[:port].nil? dnsruby_config[:use_tcp] = config[:use_tcp] unless config[:use_tcp].nil? resolv = Dnsruby::Resolver.new(dnsruby_config) resolv.do_validation = true if config[:validate] - entries = resolv.query(config[:domain], config[:type], config[:class]) - resolv.query_timeout = config[:timeout] - puts "Entries: #{entries}" if config[:debug] + entries = [] + count = 0 + while count < config[:request_count] + begin + entry = resolv.query(config[:domain], config[:type], config[:class]) + resolv.query_timeout = config[:timeout] + rescue => e + entry = e + end + entries << entry + puts "Entry #{count}: #{entry}" if config[:debug] + count += 1 + end + entries end def check_against_regex(entries, regex) # produce an Array of entry strings @@ -131,10 +156,63 @@ end end # b.each() critical "Resolved #{config[:domain]} #{config[:type]} did not match #{regex}" end + def check_results(entries) + errors = [] + success = [] + + entries.each do |entry| + if entry.class == Dnsruby::NXDomain + errors << "Could not resolve #{config[:domain]} #{config[:type]} record" + next + elsif entry.class == Dnsruby::ResolvTimeout + errors << "Could not resolve #{config[:domain]}: Query timed out" + next + elsif entry.is_a?(Exception) + errors << "Could not resolve #{config[:domain]}: #{entry}" + next + end + + puts entry.answer if config[:debug] + if entry.answer.length.zero? + success << "Could not resolve #{config[:domain]} #{config[:type]} record" + elsif config[:result] + # special logic for checking ipaddresses with result + # mostly for ipv6 but decided to use the same logic for + # consistency reasons + if config[:type] == 'A' || config[:type] == 'AAAA' + check_ips(entries) + # non ip type + else + b = if entry.answer.count > 1 + entry.answer.rrsets(config[:type].to_s).to_s + else + entry.answer.first.to_s + end + if b.include?(config[:result]) + success << "Resolved #{entry.security_level} #{config[:domain]} #{config[:type]} included #{config[:result]}" + else + errors << "Resolved #{config[:domain]} #{config[:type]} did not include #{config[:result]}" + end + end + elsif config[:regex] + check_against_regex(entry, Regexp.new(config[:regex])) + + elsif config[:validate] + if entry.security_level != 'SECURE' + error << "Resolved #{entry.security_level} #{config[:domain]} #{config[:type]}" + end + success << "Resolved #{entry.security_level} #{config[:domain]} #{config[:type]}" + else + success << "Resolved #{config[:domain]} #{config[:type]}" + end + end + [errors, success] + end + def check_ips(entries) ips = entries.answer.rrsets(config[:type]).flat_map(&:rrs).map(&:address).map(&:to_s) result = IPAddr.new config[:result] if ips.any? { |ip| (IPAddr.new ip) == result } ok "Resolved #{entries.security_level} #{config[:domain]} #{config[:type]} included #{config[:result]}" @@ -143,54 +221,19 @@ end end def run unknown 'No domain specified' if config[:domain].nil? + unknown 'Count must be 1 or more' if config[:request_count] < 1 - begin - entries = resolve_domain - rescue Dnsruby::NXDomain - output = "Could not resolve #{config[:domain]} #{config[:type]} record" - critical(output) - return - rescue => e - output = "Could not resolve #{config[:domain]}: #{e}" - config[:warn_only] ? warning(output) : critical(output) - return - end - puts entries.answer if config[:debug] - if entries.answer.length.zero? - output = "Could not resolve #{config[:domain]} #{config[:type]} record" - config[:warn_only] ? warning(output) : critical(output) - elsif config[:result] - # special logic for checking ipaddresses with result - # mostly for ipv6 but decided to use the same logic for - # consistency reasons - if config[:type] == 'A' || config[:type] == 'AAAA' - check_ips(entries) - # non ip type - else - b = if entries.answer.count > 1 - entries.answer.rrsets(config[:type].to_s).to_s - else - entries.answer.first.to_s - end - if b.include?(config[:result]) - ok "Resolved #{entries.security_level} #{config[:domain]} #{config[:type]} included #{config[:result]}" - else - critical "Resolved #{config[:domain]} #{config[:type]} did not include #{config[:result]}" - end - end + entries = resolve_domain + errors, success = check_results(entries) - elsif config[:regex] - check_against_regex(entries, Regexp.new(config[:regex])) - - elsif config[:validate] - if entries.security_level != 'SECURE' - critical "Resolved #{entries.security_level} #{config[:domain]} #{config[:type]}" - end - ok "Resolved #{entries.security_level} #{config[:domain]} #{config[:type]}" + percent = success.count.to_f / config[:request_count] * 100 + if percent < config[:threshold] + output = "#{percent.to_i}% of tests succeeded: #{errors.uniq.join(', ')}" + config[:warn_only] ? warning(output) : critical(output) else - ok "Resolved #{config[:domain]} #{config[:type]}" + ok(success.uniq.join(', ')) end end end