nagira 0.2.5

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 (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