nagira 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/History.md +89 -0
  2. data/Rakefile +128 -0
  3. data/bin/nagira +11 -0
  4. data/bin/nagira-setup +6 -0
  5. data/config/defaults.rb +74 -0
  6. data/config/environment.rb +44 -0
  7. data/config/nagira.defaults +66 -0
  8. data/config/nagira.init_d +133 -0
  9. data/lib/app.rb +330 -0
  10. data/lib/app/routes/get/config.rb +22 -0
  11. data/lib/app/routes/get/objects.rb +71 -0
  12. data/lib/app/routes/get/status.rb +153 -0
  13. data/lib/app/routes/put.rb +52 -0
  14. data/lib/app/routes/put/status.rb +139 -0
  15. data/lib/nagira.rb +55 -0
  16. data/lib/nagira/background_parse.rb +28 -0
  17. data/lib/nagira/nagios.rb +20 -0
  18. data/lib/nagira/timed_parse.rb +77 -0
  19. data/spec/00_configuration_spec.rb +62 -0
  20. data/spec/01_nagira_response_spec.rb +122 -0
  21. data/spec/02_0_status_spec.rb +53 -0
  22. data/spec/02_nagira_data_spec.rb +101 -0
  23. data/spec/03_api_spec.rb +48 -0
  24. data/spec/spec_helper.rb +4 -0
  25. data/test/benchmark.rb +26 -0
  26. data/test/data/bad/README +1 -0
  27. data/test/data/bad/nagios.cfg +1305 -0
  28. data/test/data/bad/objects.cache +1868 -0
  29. data/test/data/bad/status.dat +1766 -0
  30. data/test/data/json/GET.txt +15 -0
  31. data/test/data/json/README.txt +9 -0
  32. data/test/data/json/host_check.json +4 -0
  33. data/test/data/json/host_check.sh +4 -0
  34. data/test/data/json/ping.json +5 -0
  35. data/test/data/json/ping_and_http.json +12 -0
  36. data/test/data/json/ping_and_http_check.sh +4 -0
  37. data/test/data/json/ping_check.sh +4 -0
  38. data/test/data/nagios.cfg +1305 -0
  39. data/test/data/objects.cache +1868 -0
  40. data/test/data/status.dat +1652 -0
  41. data/version.txt +1 -0
  42. metadata +384 -0
data/lib/app.rb ADDED
@@ -0,0 +1,330 @@
1
+ # @!macro [attach] sinatra.get
2
+ #
3
+ # @overload get "$1"
4
+ #
5
+ # @return HTTP response. Hash formatted in the format defined by
6
+ # requested output type(XML, YAML or JSON).
7
+ #
8
+ #
9
+ #
10
+ # @!macro [new] type
11
+ # @param [String] :type Type is one of Nagios objects like hosts, hostgroupsroups, etc.
12
+ #
13
+ # @!macro [new] name
14
+ # @param [String] :name
15
+ #
16
+ # @!macro [new] hostname
17
+ # @param [String] :hostname Configured Nagios hostname
18
+ #
19
+ # @!macro [new] service_name
20
+ # @param [String] :service_name Configured Nagios service for the host
21
+ #
22
+ # @!macro [new] accepted
23
+ #
24
+ # <b>Accepted output type modifiers:</b>
25
+ #
26
+ # @!macro [new] list
27
+ #
28
+ # - +/_list+ : Short list of available objects, depending on the
29
+ # current request context: hosts, services, etc.
30
+ #
31
+ # @!macro [new] state
32
+ #
33
+ # - +/_state+ - Instead of full status information send only
34
+ # current state. For hosts up/down, for services OK, Warn,
35
+ # Critical, Unknown (0,1,2-1)
36
+ #
37
+ # @!macro [new] full
38
+ #
39
+ # - +/_full+ - Show full status information. When used in
40
+ # /_status/_full call will display full hoststaus and
41
+ # servicestatus information for each host.
42
+ #
43
+ #
44
+
45
+
46
+
47
+ require 'nagira'
48
+
49
+ ##
50
+ # Main class of Nagira application implementing RESTful API for
51
+ # Nagios.
52
+ #
53
+ class Nagira < Sinatra::Base
54
+
55
+ set :app_file, __FILE__
56
+
57
+ ##
58
+ # Do some necessary tasks at start and then run Sinatra app.
59
+ #
60
+ # @method startup_configuration
61
+ # @overload before("Initial Config")
62
+ configure do
63
+
64
+ $nagios = { }
65
+ $nagios[:config] = Nagios::Config.new Nagira.settings.nagios_cfg
66
+ $nagios[:config].parse
67
+
68
+ $nagios.merge!({
69
+ status: Nagios::Status.new( Nagira.settings.status_cfg ||
70
+ $nagios[:config].status_file
71
+ ),
72
+ objects: Nagios::Objects.new( Nagira.settings.objects_cfg ||
73
+ $nagios[:config].object_cache_file
74
+ ),
75
+ commands: Nagios::ExternalCommands.new( Nagira.settings.command_file ||
76
+ $nagios[:config].command_file
77
+ )
78
+ })
79
+
80
+ puts "[#{Time.now}] -- Starting Nagira application"
81
+ puts "[#{Time.now}] -- Version #{Nagira::VERSION}"
82
+ puts "[#{Time.now}] -- Running in #{Nagira.settings.environment} environment"
83
+ $nagios.keys.each do |x|
84
+ puts "[#{Time.now}] -- Using nagios #{x} file: #{$nagios[x].path}"
85
+ end
86
+
87
+ $nagios[:status].parse
88
+ $nagios[:objects].parse
89
+
90
+ @status = $nagios[:status].status['hosts']
91
+ @objects = $nagios[:objects].objects
92
+
93
+ Nagios::BackgroundParser.new
94
+ end
95
+
96
+
97
+ ##
98
+ # Parse nagios files.
99
+ #
100
+ # Note: *.parse methods are monkey-patched here (if you have required
101
+ # 'lib/nagira' above) to set min parsing interval to avoid file paring
102
+ # on each HTTP request. File is parsed only if it was changed and if
103
+ # it was parsed more then 60 (default) seconds ago. See
104
+ # +lib/nagira/timed_parse.rb+ for mor more info.
105
+ #
106
+ # In development mode use files located under +./test/data+
107
+ # directory. This allows to do development on the host where Nagios is
108
+ # notinstalled. If you want to change this edit configuration in
109
+ # config/environment.rb file.
110
+ #
111
+ # See also comments in config/default.rb file regarding nagios_cfg,
112
+ # status_cfg, objects_cfg.
113
+ #
114
+ # @method parse_nagios_files
115
+ # @overload before("Parse Nagios files")
116
+
117
+ before do
118
+
119
+ if Nagira.settings.start_background_parser
120
+ unless $bg.alive?
121
+ logger.warn "Background Parser is configured to run, but is not active"
122
+ $nagios[:config].parse
123
+ $nagios[:status].parse
124
+ $nagios[:objects].parse
125
+ end
126
+ else
127
+ $nagios[:config].parse
128
+ $nagios[:status].parse
129
+ $nagios[:objects].parse
130
+ end
131
+
132
+ @status = $nagios[:status].status['hosts']
133
+ @objects = $nagios[:objects].objects
134
+
135
+ ##
136
+ # TODO: This stuff breaks XML valid. Will have to wait.
137
+ #
138
+ # idx = 0
139
+ # @status.keys.uniq.each do |hostname|
140
+ # @status[idx] = @status[hostname]
141
+ # idx += 1
142
+ # end
143
+ #
144
+ # Add plural keys to use ActiveResource with Nagira
145
+ #
146
+ @objects.keys.each do |singular|
147
+ @objects[singular.to_s.pluralize.to_sym] = @objects[singular]
148
+ end
149
+
150
+ end
151
+
152
+ ##
153
+ # @method clear_instance_data
154
+ # @overload before("clear data")
155
+ #
156
+ # Clear values onf instance variables before start.
157
+ #
158
+ before do
159
+ @data = []
160
+ @format = @output = nil
161
+ end
162
+
163
+ ##
164
+ # @method strip_extensions
165
+ # @overload before("detect format")
166
+ #
167
+ # Detect and strip output format extension
168
+ #
169
+ # Strip extension (@format) from HTTP route and set it as instance
170
+ # variable @format. Valid formats are .xml, .json, .yaml. If format
171
+ # is not specified, it is set to default format
172
+ # (Nagira.settings.format).
173
+ #
174
+ # \@format can be assigned one of the symbols: :xml, :json, :yaml.
175
+ #
176
+ # = Examples
177
+ #
178
+ # GET /_objects # => default format
179
+ # GET /_objects.json # => :json
180
+ # GET /_status/_list.yaml # => :yaml
181
+ #
182
+ before do
183
+ request.path_info.sub!(/#{settings.format_extensions}/, '')
184
+ @format = ($1 || settings.format).to_sym
185
+ content_type "application/#{@format.to_s}"
186
+ end
187
+
188
+ ##
189
+ # @method strip_output_type
190
+ # @overload before('detect output mode')
191
+ #
192
+ # Detect output mode modifier
193
+ #
194
+ # Detect and strip output type from HTTP route. Full list of
195
+ # output types is +:list+, +:state+ or +:full+, corresponding to
196
+ # (+/list, +/state+, +/full+ routes).
197
+ #
198
+ # Output type defined by route modifier appended to the end of HTTP
199
+ # route. If no output type specfied it is set to +:full+. Output
200
+ # mode can be followed by format extension (+.json+, +.xml+ or
201
+ # +.yaml+).
202
+ #
203
+ # = Examples
204
+ #
205
+ # GET /_objects/_list # => :list
206
+ # GET /_status/_state # => :state
207
+ # GET /_status/:hostname # => :full
208
+ # GET /_status # => :normal
209
+ #
210
+ before do
211
+ request.path_info.sub!(/\/_(list|state|full)$/, '')
212
+ @output = ($1 || :normal).to_sym
213
+ end
214
+
215
+ ##
216
+ # @method find_jsonp_callback
217
+ # @overload before('find callback name')
218
+ #
219
+ # Detects if request is using jQuery JSON-P and sets @callback
220
+ # variable. @callback variable is used if after method and prepends
221
+ # JSON data with callback function name.
222
+ #
223
+ # = Example
224
+ #
225
+ # GET /_api?callback=jQuery12313123123 # @callback == jQuery12313123123
226
+ #
227
+ # JSONP support is based on the code from +sinatra/jsonp+ Gem
228
+ # https://github.com/shtirlic/sinatra-jsonp.
229
+ #
230
+ before do
231
+ if @format == :json
232
+ ['callback','jscallback','jsonp','jsoncallback'].each do |x|
233
+ @callback = params.delete(x) unless @callback
234
+ end
235
+ end
236
+ end
237
+
238
+ ##
239
+ # @method object_not_found
240
+ # @overload after("Object not found or bad request")
241
+ #
242
+ # If result-set of object/status search is empty return HTTP 404 .
243
+ # This can happen when you are requesting status for not existing
244
+ # host and/or service.
245
+ #
246
+ #
247
+ after do
248
+ # return unless request["REQUEST_METHOD"] == 'PUT'
249
+ if ! @data || @data.empty?
250
+ halt [404, {
251
+ :message => "Object not found or bad request",
252
+ :error => "HTTP::Notfound"
253
+ }.send("to_#{@format}")
254
+ ]
255
+ end
256
+ end
257
+
258
+ ##
259
+ # @method return_jsonp_data
260
+ # @overload after("Return formatted data")
261
+ #
262
+ # If it's a JSON-P request, return its data with prepended @callback
263
+ # function name. JSONP request is detected by +before+ method.
264
+ #
265
+ # If no callback paramete given, then simply return formatted data
266
+ # as XML, JSON, or YAML in response body.
267
+ #
268
+ # = Example
269
+ #
270
+ # $ curl 'http://localhost:4567/?callback=test'
271
+ # test(["{\"application\":\"Nagira\",\"version\":\"0.1.3\",\"url\":\"http://dmytro.github.com/nagira/\"}"])
272
+ #
273
+ after do
274
+ body( @callback ? "#{@callback.to_s} (#{@data.to_json})" : @data.send("to_#{@format}") )
275
+ end
276
+
277
+
278
+
279
+ ##
280
+ # @method get_api
281
+ # @overload get(/_api)
282
+ #
283
+ # Provide information about API routes
284
+ #
285
+ get "/_api" do
286
+ @data = self.api
287
+ nil
288
+ end
289
+
290
+
291
+ ##
292
+ # @method get_runtime_config
293
+ # @overload get(/_runtime)
294
+ #
295
+ # Print out nagira runtime configuration
296
+ get "/_runtime" do
297
+ @data = {
298
+ application: self.class,
299
+ version: VERSION,
300
+ runtime: {
301
+ environment: Nagira.settings.environment,
302
+ home: ENV['HOME'],
303
+ user: ENV['LOGNAME'],
304
+ nagiosFiles: $nagios.keys.map { |x| { x => $nagios[x].path }}
305
+ }
306
+ }
307
+ nil
308
+ end
309
+
310
+ # @method get_slash
311
+ # @overload get(/)
312
+ #
313
+ # Returns application information: name, version, github repository.
314
+ get "/" do
315
+ @data = {
316
+ :application => self.class,
317
+ :version => VERSION,
318
+ :source => GITHUB,
319
+ :apiUrl => request.url.sub(/\/$/,'') + "/_api",
320
+ }
321
+ nil
322
+ end
323
+ # Other resources in parsed status file. Supported are => ["hosts",
324
+ # "info", "process", "contacts"]
325
+ # get "/:resource" do |resource|
326
+ # respond_with $nagios.status[resource], @format
327
+ # end
328
+
329
+ end
330
+
@@ -0,0 +1,22 @@
1
+ class Nagira < Sinatra::Base
2
+
3
+ # Config routes
4
+ # ============================================================
5
+
6
+ # @!macro [attach] sinatra.get
7
+ #
8
+ # @overload get "$1"
9
+ #
10
+ # @return HTTP response. Hash formatted in the format defined by
11
+ # requested output type(XML, YAML or JSON).
12
+ #
13
+ #
14
+ # @method get_config
15
+ #
16
+ # Get Nagios configuration hash form parsing main Nagios
17
+ # configuration file nagios.cfg
18
+ get "/_config" do
19
+ @data = $nagios[:config].configuration
20
+ nil
21
+ end
22
+ end
@@ -0,0 +1,71 @@
1
+ class Nagira < Sinatra::Base
2
+ #
3
+ # Objects routes
4
+ # ============================================================
5
+
6
+ ##
7
+ # @method get_objects
8
+ #
9
+ # Get full list of Nagios objects. Returns hash containing all
10
+ # configured objects in Nagios: hosts, hostgroups, services,
11
+ # contacts. etc.
12
+ #
13
+ # @macro accepted
14
+ # @macro list
15
+ #
16
+ get "/_objects" do
17
+
18
+ @data = begin
19
+ @output == :list ? @objects.keys : @objects
20
+ rescue NoMethodError
21
+ nil
22
+ end
23
+ nil
24
+ end
25
+
26
+ ##
27
+ # @method get_object_type
28
+ # @!macro type
29
+ #
30
+ # Get all objects of type :type
31
+ #
32
+ # @!macro accepted
33
+ # @!macro list
34
+ #
35
+ #
36
+ get "/_objects/:type" do |type|
37
+ begin
38
+ if type.singularize == type
39
+ @data = @objects[type.to_sym]
40
+ else
41
+ @data = Array[@objects[type.to_sym]]
42
+ end
43
+ @data = @data.keys if @output == :list
44
+ rescue NoMethodError
45
+ nil
46
+ end
47
+
48
+ nil
49
+ end
50
+
51
+ ##
52
+ # @method get_1_object
53
+ #
54
+ # Get single Nagios object.
55
+ #
56
+ # @!macro type
57
+ # @!macro name
58
+ #
59
+ # @!macro accepted
60
+ # * none
61
+ #
62
+ get "/_objects/:type/:name" do |type,name|
63
+ begin
64
+ @data = @objects[type.to_sym][name]
65
+ rescue NoMethodError
66
+ nil
67
+ end
68
+
69
+ nil
70
+ end
71
+ end
@@ -0,0 +1,153 @@
1
+ class Nagira < Sinatra::Base
2
+ # @!macro [attach] sinatra.get
3
+ #
4
+ # @overload get "$1"
5
+ #
6
+ # @return HTTP response. Hash formatted in the format defined by
7
+ # requested output type(XML, YAML or JSON).
8
+ #
9
+ #
10
+ #
11
+ # @!macro [new] type
12
+ # @param [String] :type Type is one of Nagios objects like hosts, hostgroupsroups, etc.
13
+ #
14
+ # @!macro [new] name
15
+ # @param [String] :name
16
+ #
17
+ # @!macro [new] hostname
18
+ # @param [String] :hostname Configured Nagios hostname
19
+ #
20
+ # @!macro [new] service_name
21
+ # @param [String] :service_name Configured Nagios service for the host
22
+ #
23
+ # @!macro [new] accepted
24
+ #
25
+ # <b>Accepted output type modifiers:</b>
26
+ #
27
+ # @!macro [new] list
28
+ #
29
+ # - +/_list+ : Short list of available objects, depending on the
30
+ # current request context: hosts, services, etc.
31
+ #
32
+ # @!macro [new] state
33
+ #
34
+ # - +/_state+ - Instead of full status information send only
35
+ # current state. For hosts up/down, for services OK, Warn,
36
+ # Critical, Unknown (0,1,2-1)
37
+ #
38
+ # @!macro [new] full
39
+ #
40
+ # - +/_full+ - Show full status information. When used in
41
+ # /_status/_full call will display full hoststaus and
42
+ # servicestatus information for each host.
43
+ #
44
+ #
45
+
46
+ # Status routes
47
+ # ============================================================
48
+
49
+ ##
50
+ # @method get_status_hostname_services_service_name
51
+ #
52
+ # @!macro hostname
53
+ # @!macro service_name
54
+ #
55
+ # Full or short status information for single service on single
56
+ # host.
57
+ #
58
+ # @!macro accepted
59
+ # @!macro state
60
+ #
61
+ get "/_status/:hostname/_services/:service_name" do |hostname,service|
62
+
63
+
64
+ hostname = hostname.to_i if hostname =~ /^\d+$/
65
+ if @output == :state
66
+ @data = @status[hostname]['servicestatus'][service].slice("hostname", "service_description", "current_state")
67
+ else
68
+ @data = @status[hostname]['servicestatus'][service]
69
+ end
70
+ nil
71
+ end
72
+
73
+ ##
74
+ # @method get_status_hostname_services
75
+ # @!macro hostname
76
+ #
77
+ # All services for single host.
78
+ #
79
+ # @!macro accepted
80
+ # @!macro state
81
+ # @!macro list
82
+ # @!macro full
83
+ get "/_status/:hostname/_services" do |hostname|
84
+
85
+ hostname = hostname.to_i if hostname =~ /^\d+$/
86
+ case @output
87
+ when :list
88
+ @data = @status[hostname]['servicestatus'].keys
89
+ when :state
90
+ @data = @status.each { |k,v| @data[k] = v.slice("host_name", "service_description", "current_state") }
91
+ else
92
+ @data = @status[hostname]['servicestatus']
93
+ end
94
+
95
+ nil
96
+ end
97
+
98
+ # Hoststatus for single host
99
+ #
100
+ # @method get_status_hostname
101
+ #
102
+ # @!macro hostname
103
+ #
104
+ # @!macro accepted
105
+ # @!macro state
106
+ #
107
+ # Support for:
108
+ # - plural resources: N/A
109
+ # - object access by ID: NO (TODO)
110
+
111
+ get "/_status/:hostname" do |hostname|
112
+
113
+ hostname = hostname.to_i if hostname =~ /^\d+$/
114
+ @data = @status[hostname]['hoststatus'].dup if @status.has_key? hostname
115
+
116
+ if @output == :state
117
+ @data = @data.slice("host_name", "current_state")
118
+ end
119
+
120
+ nil
121
+ end
122
+
123
+ ##
124
+ # @method get_status
125
+ #
126
+ # All hosts status. If no output modifier provided, outputs full hosttatus information for each host. Not including services information.
127
+ #
128
+ # @!macro accepted
129
+ # @!macro state
130
+ # @!macro list
131
+ # @!macro full
132
+ #
133
+ # Support for (see API):
134
+ # - plural resources: N/A
135
+ # - object access by ID: N/A
136
+
137
+ get "/_status" do
138
+ @data = @status.dup
139
+
140
+ case @output
141
+ when :state
142
+ @data.each { |k,v| @data[k] = v['hoststatus'].slice("host_name", "current_state") }
143
+ when :list
144
+ @data = @data.keys
145
+ when :full
146
+ @data
147
+ else
148
+ @data.each { |k,v| @data[k] = v['hoststatus'] }
149
+ end
150
+
151
+ nil
152
+ end
153
+ end