9d05ffff7f94a6efb9a3b07bbf6b9e4088bc4e63
[mirror/dsa-nagios.git] / dsa-nagios-checks / dsa-check-soas
1 #!/usr/bin/ruby
2
3 # Copyright 2006 Peter Palfrader
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 require 'resolv'
25 require 'optparse'
26 require 'yaml'
27
28 NAGIOS_STATUS = { :OK => 0, :WARNING => 1, :CRITICAL => 2, :UNKNOWN => -1 };
29 @verbose = 0;
30 @additional_nameservers = []
31
32 def show_help(parser, code=0, io=STDOUT)
33   program_name = File.basename($0, '.*')
34   io.puts "Usage: #{program_name} [options] <domainname> [<domainname> ...]"
35   io.puts parser.summarize
36   exit(code)
37 end
38 ARGV.options do |opts|
39         opts.on_tail("-h", "--help" , "Display this help screen")                { show_help(opts) }
40         opts.on("-v", "--verbose"   , String, "Be verbose")                      { @verbose += 1 }
41         opts.on("-a", "--add=HOST"  , String, "Also check SOA on <nameserver>")  { |val| @additional_nameservers << val }
42         opts.parse!
43 end
44 show_help(ARGV.options, 1, STDERR) if ARGV.length == 0
45
46 warnings = []
47 oks = []
48
49 dns = Resolv::DNS.new
50 ARGV.each{ |domain|
51         serial = []
52         nameservers = dns.getresources(domain, Resolv::DNS::Resource::IN::NS)
53         nameservernames = nameservers.collect{ |ns| ns.name.to_s }
54         nameservernames = nameservernames.concat @additional_nameservers
55         nameservernames.each{ |nameserver|
56                 puts "Testing nameserver #{nameserver} for #{domain}" if @verbose > 0
57                 arecords = dns.getresources(nameserver, Resolv::DNS::Resource::IN::A)
58                 warnings << "Nameserver #{nameserver} for #{domain} has #{arecords.length} A records" if arecords.length != 1
59                 arecords.each{ |a|
60                         puts " Nameserver #{nameserver} is at #{a.address}" if @verbose > 0
61                         begin
62                                 resolver = Resolv::DNS.new({:nameserver => a.address.to_s})
63                                 soas = resolver.getresources(domain, Resolv::DNS::Resource::IN::SOA)
64                         rescue SystemCallError => e
65                                 warnings << "Could not resolve #{domain} on #{nameserver}: #{e.message}"
66                         else
67                                 resolver.close
68                                 warnings << "Nameserver #{nameserver} for #{domain} returns #{soas.length} SOAs" if soas.length != 1
69                                 soas.each{ |soa|
70                                         puts " Nameserver #{nameserver} returns serial #{soa.serial} for #{domain}" if @verbose > 0
71                                         serial << soa.serial unless serial.include? soa.serial
72                                 }
73                         end
74                 }
75         }
76         case serial.length
77                 when 0
78                         warnings << "Found no serials for #{domain}"
79                 when 1
80                         oks << "#{domain} is at #{serial.first}"
81                 else
82                         warnings << "Nameservers disagree on serials for #{domain}: found #{serial.join(', ')}" if serial.length != 1
83         end
84 }
85 dns.close
86
87 if warnings.length > 0
88         puts warnings.join('; ')
89         exit NAGIOS_STATUS[:WARNING]
90 else
91         puts oks.join('; ')
92         exit NAGIOS_STATUS[:OK]
93 end