nagios-manage 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. data/bin/check_check.rb +153 -0
  2. data/bin/nagsrv.rb +149 -0
  3. data/lib/nagios/status.rb +268 -0
  4. metadata +71 -0
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Check check- aggregate results from other checks in your nagios instance.
4
+ # Reads the 'status_file' for current states.
5
+ #
6
+ # Useful for having lots of small checks roll up into an aggregate that
7
+ # only alerts you once during failures, not N times.
8
+ #
9
+ # Also useful for business-view monitoring
10
+ #
11
+
12
+ require "rubygems"
13
+ require "nagios/status"
14
+ require "optparse"
15
+
16
+ class Nagios::Status::Model
17
+ STATEMAP = {
18
+ "0" => "OK",
19
+ "1" => "WARNING",
20
+ "2" => "CRITICAL",
21
+ "3" => "UNKNOWN",
22
+ }
23
+
24
+ def initialize(path)
25
+ @path = path
26
+ @status = Nagios::Status.new
27
+ update
28
+ end # def initialize
29
+
30
+ def update
31
+ @status.parsestatus(@path)
32
+ end # def update
33
+
34
+ def services(service_pattern=nil, host_pattern=nil)
35
+ matches = []
36
+ self.hosts(host_pattern).each do |host, hostinfo|
37
+ hostinfo["servicestatus"].each do |name, status|
38
+ next if service_pattern and !service_pattern.match(name)
39
+
40
+ # Skip silenced or checks in scheduled downtime.
41
+ next if status["notifications_enabled"].to_i == 0
42
+ next if status["scheduled_downtime_depth"].to_i > 0
43
+
44
+ # Only report checks that are in 'hard' state.
45
+ # If not in hard state, report 'last_hard_state' instead.
46
+ if status["state_type"] != "1" # not in hard state
47
+ status["current_state"] = status["last_hard_state"]
48
+ # TODO(sissel): record that this service is currently
49
+ # in a soft state transition.
50
+ end
51
+
52
+ # TODO(sissel): Maybe also skip checks that are 'acknowledged'
53
+ matches << status
54
+ end
55
+ end # hosts().each
56
+ return matches
57
+ end # def services
58
+
59
+ def hosts(pattern=nil)
60
+ if pattern
61
+ return @status.status["hosts"]
62
+ #.reject { |name,hostinfo| !pattern.match(name) }
63
+ else
64
+ return @status.status["hosts"]
65
+ end # if pattern
66
+ end # def hosts
67
+
68
+ # TODO(sissel): add a proper 'status' model that
69
+ # has HostStatus, ServiceStatus, etc.
70
+
71
+ end # class Nagios::Status::Model
72
+
73
+ Settings = Struct.new(:nagios_cfg, :status_path, :service_pattern, :host_pattern)
74
+ def main(args)
75
+ progname = File.basename($0)
76
+ settings = Settings.new
77
+ settings.nagios_cfg = "/etc/nagios3/nagios.cfg" # debian/ubuntu default
78
+
79
+ opts = OptionParser.new do |opts|
80
+ opts.banner = "Usage: #{progname} [options]"
81
+
82
+ opts.on("-f NAGIOS_CFG", "--config NAGIOS_CFG",
83
+ "Path to your nagios.cfg (I will use the status_file setting") do |val|
84
+ settings.nagios_cfg = val
85
+ end
86
+
87
+ opts.on("-s REGEX", "--service REGEX",
88
+ "Aggregate only services matching the given pattern") do |val|
89
+ settings.service_pattern = val
90
+ end
91
+
92
+ opts.on("-h REGEX", "--host REGEX",
93
+ "Aggregate only services from hosts matching the given pattern") do |val|
94
+ settings.host_pattern = val
95
+ end
96
+ end # OptionParser.new
97
+
98
+ opts.parse!(args)
99
+
100
+ # hacky parsing, for now
101
+ status_line = File.new(settings.nagios_cfg, "r").readlines.grep(/^\s*status_file\s*=/).first.chomp
102
+ settings.status_path = status_line.split(/\s*=\s*/)[1]
103
+ status = Nagios::Status::Model.new(settings.status_path)
104
+
105
+ results = Hash.new { |h,k| h[k] = 0 }
106
+ service_pattern = nil
107
+ if settings.service_pattern
108
+ service_pattern = Regexp.new(settings.service_pattern)
109
+ end
110
+
111
+ host_pattern = nil
112
+ if settings.host_pattern
113
+ host_pattern = Regexp.new(settings.host_pattern)
114
+ end
115
+
116
+ Nagios::Status::Model::STATEMAP.values.each do |state|
117
+ results[state] = []
118
+ end
119
+
120
+ # Collect check results by state
121
+ status.services(service_pattern, host_pattern).each do |service_status|
122
+ state = Nagios::Status::Model::STATEMAP[service_status["current_state"]]
123
+ if state == nil
124
+ state = "UNKNOWN(state=#{service_status["current_state"]})"
125
+ end
126
+
127
+ results[state] << service_status
128
+ end
129
+
130
+ # Output a summary line
131
+ ["OK", "WARNING", "CRITICAL", "UNKNOWN"].each do | state|
132
+ print "#{state}=#{results[state].length} "
133
+ end
134
+ print "services=/#{settings.service_pattern}/ "
135
+ print "hosts=/#{settings.host_pattern}/ "
136
+ puts
137
+
138
+ # More data output
139
+ ["WARNING", "CRITICAL", "UNKNOWN"].each do |state|
140
+ if results[state] && results[state].count > 0
141
+ puts "Services in #{state}:"
142
+ results[state].sort { |a,b| a["host_name"] <=> b["host_name"] }.each do |service|
143
+ puts " #{service["host_name"]} => #{service["service_description"]}"
144
+ end
145
+ end # if results[state]
146
+ end # for each non-OK state
147
+
148
+
149
+ #total = results.values.reduce(0) { |sum, val| sum += val }
150
+ return 0
151
+ end
152
+
153
+ exit(main(ARGV))
data/bin/nagsrv.rb ADDED
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # A tool to do mass operations on nagios services. It is intended to be run on
4
+ # the server that hosts nagios and it needs read access to the status.log file
5
+ # typically found in the var dir.
6
+ #
7
+ # Command options are broken up into several types:
8
+ #
9
+ # == General
10
+ # --statusfile
11
+ # Where to find the status file
12
+ #
13
+ # == Output Selectors
14
+ # --list-hosts
15
+ # List hostnames that match certain criteria
16
+ #
17
+ # --list-service
18
+ # List services that match certain criteria
19
+ #
20
+ # == Selectors
21
+ # --with-service
22
+ # Pass a specific service name or a regex in the form
23
+ # /pattern/ if you pass a regex you can only pass this
24
+ # option once, if you pass specific services you can
25
+ # use this option many times the services will be searches
26
+ # in an OR fasion
27
+ #
28
+ # --for-host
29
+ # Restrict the selection of services to a specific host or
30
+ # regex match of hosts, same regex rules as for --with-service
31
+ #
32
+ # --notify-enabled
33
+ # List only services with notifications enabled, in this mode
34
+ # the output will be in the form host:service
35
+ #
36
+ # == Actions
37
+ # --enable-notify / --disable-notify
38
+ # Enable or Disable notifications for selected services
39
+ #
40
+ # --enable-checks / --disable-checks
41
+ # Enable of Disable checks for selected services
42
+ #
43
+ # --force-check
44
+ # Force checks for selected services
45
+ #
46
+ # --acknowledge
47
+ # Ackknowledge services without sending notifies
48
+ #
49
+ # Released under the terms of the Apache version 2
50
+ # license
51
+ #
52
+ # Please open an issue at ruby-nagios.googlecode.com
53
+ # with any queries
54
+
55
+ require 'nagios/status.rb'
56
+
57
+ require 'getoptlong'
58
+
59
+ def showhelp
60
+ begin
61
+ require 'rdoc/ri/ri_paths'
62
+ require 'rdoc/usage'
63
+ RDoc::usage
64
+ rescue LoadError
65
+ puts ("Install RDoc::usage or view the comments in the top of the script to get detailed help")
66
+ end
67
+ end
68
+
69
+ opts = GetoptLong.new(
70
+ [ '--statusfile', '-s', GetoptLong::REQUIRED_ARGUMENT],
71
+ [ '--list-hosts', GetoptLong::NO_ARGUMENT],
72
+ [ '--list-services', GetoptLong::NO_ARGUMENT],
73
+ [ '--notify-enabled', GetoptLong::NO_ARGUMENT],
74
+ [ '--notify-disabled', GetoptLong::NO_ARGUMENT],
75
+ [ '--for-host', GetoptLong::REQUIRED_ARGUMENT],
76
+ [ '--with-service', GetoptLong::REQUIRED_ARGUMENT],
77
+ [ '--enable-notify', GetoptLong::NO_ARGUMENT],
78
+ [ '--disable-notify', GetoptLong::NO_ARGUMENT],
79
+ [ '--enable-checks', GetoptLong::NO_ARGUMENT],
80
+ [ '--disable-checks', GetoptLong::NO_ARGUMENT],
81
+ [ '--force-check', GetoptLong::NO_ARGUMENT],
82
+ [ '--acknowledge', GetoptLong::NO_ARGUMENT]
83
+ )
84
+
85
+ statusfile = "status.log"
86
+ listhosts = false
87
+ withservice = []
88
+ listservices = false
89
+ forhost = []
90
+ notify = nil
91
+ action = nil
92
+ options = nil
93
+
94
+ begin
95
+ opts.each do |opt, arg|
96
+ case opt
97
+ when "--statusfile"
98
+ statusfile = arg
99
+ when "--list-hosts"
100
+ listhosts = true
101
+ when "--list-services"
102
+ listservices = true
103
+ when "--with-service"
104
+ withservice << arg
105
+ when "--for-host"
106
+ forhost << arg
107
+ when "--enable-notify"
108
+ action = "[${tstamp}] ENABLE_SVC_NOTIFICATIONS;${host};${service}"
109
+ when "--disable-notify"
110
+ action = "[${tstamp}] DISABLE_SVC_NOTIFICATIONS;${host};${service}"
111
+ when "--force-check"
112
+ action = "[${tstamp}] SCHEDULE_FORCED_SVC_CHECK;${host};${service};${tstamp}"
113
+ when "--enable-checks"
114
+ action = "[${tstamp}] ENABLE_SVC_CHECK;${host};${service};${tstamp}"
115
+ when "--disable-checks"
116
+ action = "[${tstamp}] DISABLE_SVC_CHECK;${host};${service};${tstamp}"
117
+ when "--acknowledge"
118
+ action = "[${tstamp}] ACKNOWLEDGE_SVC_PROBLEM;${host};${service};1;0;1;#{ENV['USER']};Acknowledged from CLI"
119
+ when "--notify-enabled"
120
+ notify = 1
121
+ when "--notify-disabled"
122
+ notify = 0
123
+ end
124
+ end
125
+ rescue
126
+ showhelp
127
+ exit 1
128
+ end
129
+
130
+
131
+ nagios = Nagios::Status.new
132
+
133
+ nagios.parsestatus(statusfile)
134
+
135
+
136
+ # We want hosts so abuse the action field to print just the hostname
137
+ # and select all hosts unless other action/forhost was desigred then
138
+ # this really is just a noop and it reverts to noral behaviour
139
+ if listhosts
140
+ action = "${host}" if action == nil
141
+ forhost = "/." if forhost.size == 0
142
+ end
143
+
144
+ options = {:forhost => forhost, :notifyenabled => notify, :action => action, :withservice => withservice}
145
+ services = nagios.find_services(options)
146
+
147
+ puts services.join("\n")
148
+
149
+ # vi:tabstop=4:expandtab:ai
@@ -0,0 +1,268 @@
1
+ module Nagios
2
+ class Status
3
+ attr_reader :status
4
+
5
+ # Parses a nagios status file returning a data structure for all the data
6
+ def parsestatus(statusfile)
7
+ @status = {}
8
+ @status["hosts"] = {}
9
+
10
+ handler = ""
11
+ blocklines = []
12
+
13
+ File.readlines(statusfile).each do |line|
14
+ # start of new sections
15
+ if line =~ /(\w+) \{/
16
+ blocklines = []
17
+ handler = $1
18
+ end
19
+
20
+ # gather all the lines for the block into an array
21
+ # we'll pass them to a handler for this kind of block
22
+ if line =~ /\s+(\w+)=(.+)/ && handler != ""
23
+ blocklines << line
24
+ end
25
+
26
+ # end of a section
27
+ if line =~ /\}/ && handler != ""
28
+ eval("handle_#{handler}(blocklines)")
29
+ handler = ""
30
+ end
31
+ end
32
+ end
33
+
34
+ # Returns a list of all hosts matching the options in options
35
+ def find_hosts(options = {})
36
+ forhost = options.fetch(:forhost, [])
37
+ notifications = options.fetch(:notifyenabled, nil)
38
+ action = options.fetch(:action, nil)
39
+ withservice = options.fetch(:withservice, [])
40
+
41
+ hosts = []
42
+ searchquery = []
43
+
44
+ # Build up a search query for find_with_properties each
45
+ # array member is a hash of property and a match
46
+ forhost.each do |host|
47
+ searchquery << search_term("host_name", host)
48
+ end
49
+
50
+ withservice.each do |s|
51
+ searchquery << search_term("service_description", s)
52
+ end
53
+
54
+ searchquery << {"notifications_enabled" => notifications.to_s} if notifications
55
+
56
+ hsts = find_with_properties(searchquery)
57
+
58
+ hsts.each do |host|
59
+ host_name = host["host_name"]
60
+
61
+ hosts << parse_command_template(action, host_name, "", host_name)
62
+ end
63
+
64
+ hosts.uniq.sort
65
+ end
66
+
67
+ # Returns a list of all services matching the options in options
68
+ def find_services(options = {})
69
+ forhost = options.fetch(:forhost, [])
70
+ notifications = options.fetch(:notifyenabled, nil)
71
+ action = options.fetch(:action, nil)
72
+ withservice = options.fetch(:withservice, [])
73
+
74
+ services = []
75
+ searchquery = []
76
+
77
+ # Build up a search query for find_with_properties each
78
+ # array member is a hash of property and a match
79
+ forhost.each do |host|
80
+ searchquery << search_term("host_name", host)
81
+ end
82
+
83
+ withservice.each do |s|
84
+ searchquery << search_term("service_description", s)
85
+ end
86
+
87
+ searchquery << {"notifications_enabled" => notifications.to_s} if notifications
88
+
89
+ svcs = find_with_properties(searchquery)
90
+
91
+ svcs.each do |service|
92
+ service_description = service["service_description"]
93
+ host_name = service["host_name"]
94
+
95
+ # when printing services with notifications en/dis it makes
96
+ # most sense to print them in host:service format, abuse the
97
+ # action option to get this result
98
+ action = "${host}:${service}" if (notifications != nil && action == nil)
99
+
100
+ services << parse_command_template(action, host_name, service_description, service_description)
101
+ end
102
+
103
+ services.uniq.sort
104
+ end
105
+
106
+ private
107
+
108
+ # Add search terms, does all the mangling of regex vs string and so on
109
+ def search_term(haystack, needle)
110
+ needle = Regexp.new(needle.gsub("\/", "")) if needle.match("^/")
111
+ {haystack => needle}
112
+ end
113
+
114
+ # Return service blocks for each service that matches any options like:
115
+ #
116
+ # "host_name" => "foo.com"
117
+ #
118
+ # The 2nd parameter can be a regex too.
119
+ def find_with_properties(search)
120
+ services = []
121
+ query = []
122
+
123
+ query << search if search.class == Hash
124
+ query = search if search.class == Array
125
+
126
+ @status["hosts"].each do |host,v|
127
+ find_host_services(host) do |service|
128
+ matchcount = 0
129
+
130
+ query.each do |q|
131
+ q.each do |option, match|
132
+ if match.class == Regexp
133
+ matchcount += 1 if service[option].match(match)
134
+ else
135
+ matchcount += 1 if service[option] == match.to_s
136
+ end
137
+ end
138
+ end
139
+
140
+ if matchcount == query.size
141
+ services << service
142
+ end
143
+ end
144
+ end
145
+
146
+ services
147
+ end
148
+
149
+ # yields the hash for each service on a host
150
+ def find_host_services(host)
151
+ if @status["hosts"][host].has_key?("servicestatus")
152
+ @status["hosts"][host]["servicestatus"].each do |s, v|
153
+ yield(@status["hosts"][host]["servicestatus"][s])
154
+ end
155
+ end
156
+ end
157
+
158
+ # Parses a template given with a nagios command string and populates vars
159
+ # else return the string given in default
160
+ def parse_command_template(template, host, service, default)
161
+ if template.nil?
162
+ default
163
+ else
164
+ template.gsub(/\$\{host\}/, host).gsub(/\$\{service\}/, service).gsub(/\$\{tstamp\}/, Time.now.to_i.to_s)
165
+ end
166
+ end
167
+
168
+ # Figures out the service name from a block in a nagios status file
169
+ def get_service_name(lines)
170
+ if s = lines.grep(/\s+service_description=(\w+)/).first
171
+ if s =~ /service_description=(.+)$/
172
+ service = $1
173
+ else
174
+ raise("Cant't parse service in block: #{s}")
175
+ end
176
+ else
177
+ raise("Cant't find a hostname in block")
178
+ end
179
+
180
+ service
181
+ end
182
+
183
+ # Figures out the host name from a block in a nagios status file
184
+ def get_host_name(lines)
185
+ if h = lines.grep(/\s+host_name=(\w+)/).first
186
+ if h =~ /host_name=(.+)$/
187
+ host = $1
188
+ else
189
+ raise("Cant't parse hostname in block: #{h}")
190
+ end
191
+ else
192
+ raise("Cant't find a hostname in block")
193
+ end
194
+
195
+ host
196
+ end
197
+
198
+ # Parses an info block
199
+ def handle_info(lines)
200
+ @status["info"] = {} unless @status["info"]
201
+
202
+ lines.each do |l|
203
+ if l =~ /\s+(\w+)=(\w+)/
204
+ @status["info"][$1] = $2
205
+ end
206
+ end
207
+ end
208
+
209
+ # Parses a servicestatus block
210
+ def handle_servicestatus(lines)
211
+ host = get_host_name(lines)
212
+ service = get_service_name(lines)
213
+
214
+ @status["hosts"][host] = {} unless @status["hosts"][host]
215
+ @status["hosts"][host]["servicestatus"] = {} unless @status["hosts"][host]["servicestatus"]
216
+ @status["hosts"][host]["servicestatus"][service] = {} unless @status["hosts"][host]["servicestatus"][service]
217
+
218
+ lines.each do |l|
219
+ if l =~ /\s+(\w+)=(.+)$/
220
+ if $1 == "host_name"
221
+ @status["hosts"][host]["servicestatus"][service][$1] = host
222
+ else
223
+ @status["hosts"][host]["servicestatus"][service][$1] = $2
224
+ end
225
+ end
226
+ end
227
+ end
228
+
229
+ # Parses a servicestatus block
230
+ def handle_contactstatus(lines)
231
+ end
232
+
233
+ # Parses a servicecomment block
234
+ def handle_servicecomment(lines)
235
+ end
236
+
237
+ # Parses hostcomment block
238
+ def handle_hostcomment(lines)
239
+ end
240
+
241
+ # Parses a programstatus block
242
+ def handle_programstatus(lines)
243
+ @status["process"] = {} unless @status["process"]
244
+
245
+ lines.each do |l|
246
+ if l =~ /\s+(\w+)=(\w+)/
247
+ @status["process"][$1] = $2
248
+ end
249
+ end
250
+ end
251
+
252
+ # Parses a hoststatus block
253
+ def handle_hoststatus(lines)
254
+ host = get_host_name(lines)
255
+
256
+ @status["hosts"][host] = {} unless @status["hosts"][host]
257
+ @status["hosts"][host]["hoststatus"] = {} unless @status["hosts"][host]["hoststatus"]
258
+
259
+ lines.each do |l|
260
+ if l =~ /\s+(\w+)=(\w+)/
261
+ @status["hosts"][host]["hoststatus"][$1] = $2
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ # vi:tabstop=4:expandtab:ai:filetype=ruby
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nagios-manage
3
+ version: !ruby/object:Gem::Version
4
+ hash: 11
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 5
9
+ - 0
10
+ version: 0.5.0
11
+ platform: ruby
12
+ authors:
13
+ - R.I. Pienaar, Jordan Sissel
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-30 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Silence alerts, aggregate existing checks, etc.
23
+ email: rip@devco.net, jls@semicomplete.com
24
+ executables:
25
+ - check_check.rb
26
+ - nagsrv.rb
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - lib/nagios/status.rb
33
+ - bin/check_check.rb
34
+ - bin/nagsrv.rb
35
+ has_rdoc: true
36
+ homepage: http://code.google.com/p/ruby-nagios/
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.7
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: nagios-manage - a ruby tool for managing your nagios instance
70
+ test_files: []
71
+