Class: Nagira

Inherits:
Sinatra::Base
  • Object
show all
Defined in:
lib/app.rb,
lib/nagira.rb,
lib/app/routes/put.rb,
lib/app/routes/put/host.rb,
lib/app/routes/get/status.rb,
lib/app/routes/put/status.rb,
lib/app/routes/get/config.rb,
lib/app/routes/get/objects.rb

Overview

PUT method routes for services status.

Constant Summary

VERSION =
File.read(File.expand_path(File.dirname(__FILE__)) + '/../version.txt').strip
GITHUB =
"http://dmytro.github.com/nagira/"
AR_PREFIX =

When this prefix added to routes convert output to ActiveResource compatible format (basically Array instead of Hash).

"/ar"

Instance Method Summary (collapse)

Instance Method Details

- (Object) api

Get all routes that Nagira provides.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/nagira.rb', line 41

def api 
  api = { }
  
  param_regex = Regexp.new '\(\[\^\\\\\/\?\#\]\+\)'
  Nagira.routes.keys.each do |method|
    api[method] ||= []
    Nagira.routes[method].each do |r|
      path = r[0].inspect[3..-3]
      r[1].each do |parm|
        path.sub!(param_regex,":#{parm}")
      end
      path.gsub!('\\','')
      api[method] << path unless path.empty? 
    end
  end
  api
end

- (Object) after("ArgumentError")

Return 400 if result of PUT operation is not success.



284
285
286
287
# File 'lib/app.rb', line 284

after do 
  return unless request.put?
  halt [400, @data.send("to_#{@format}") ] if ! @data[:result]
end

- (Object) before("clear data")

Clear values onf instance variables before start.



171
172
173
174
# File 'lib/app.rb', line 171

before do
  @data = []
  @format = @output = nil
end

- (Object) after("Return Array for ActiveResouce routes")



295
296
297
# File 'lib/app.rb', line 295

after do
  @data = @data.values if @active_resource && @data.is_a?(Hash)
end

- (Object) before('find callback name')

Detects if request is using jQuery JSON-P and sets @callback variable. @callback variable is used if after method and prepends JSON data with callback function name.

Example

GET /_api?callback=jQuery12313123123 # @callback == jQuery12313123123

JSONP support is based on the code from sinatra/jsonp Gem github.com/shtirlic/sinatra-jsonp.



249
250
251
252
253
254
255
# File 'lib/app.rb', line 249

before do
  if @format == :json
    ['callback','jscallback','jsonp','jsoncallback'].each do |x|
      @callback = params.delete(x) unless @callback
    end
  end
end

- (Object) get_1_object

Get single Nagios object.

Accepted output type modifiers:

  • none

Parameters:

  • :type (String)

    Type is one of Nagios objects like hosts, hostgroupsroups, etc.

  • :name (String)


59
60
61
62
63
64
65
66
67
# File 'lib/app/routes/get/objects.rb', line 59

get "/_objects/:type/:name" do |type,name|
  begin
    @data = @objects[type.to_sym][name]
  rescue NoMethodError
    nil
  end

  nil
end

- (Object) get(/_api))

Provide information about API routes



327
328
329
330
# File 'lib/app.rb', line 327

get "/_api" do
  @data = self.api
  nil
end

- (Object) get("/_config")

Get Nagios configuration hash form parsing main Nagios configuration file nagios.cfg



20
21
22
23
# File 'lib/app/routes/get/config.rb', line 20

get "/_config" do
  @data = $nagios[:config].configuration
  nil
end

- (Object) get_object_type

Get all objects of type :type

Accepted output type modifiers:

  • /_list : Short list of available objects, depending on the current request context: hosts, services, etc.

Parameters:

  • :type (String)

    Type is one of Nagios objects like hosts, hostgroupsroups, etc.



36
37
38
39
40
41
42
43
44
45
46
# File 'lib/app/routes/get/objects.rb', line 36

get "/_objects/:type" do |type|
  begin
    @data = @objects[type.to_sym]
    @data = @data.keys if @output == :list

  rescue NoMethodError
    nil
  end

  nil
end

- (Object) get_objects

Get full list of Nagios objects. Returns hash containing all configured objects in Nagios: hosts, hostgroups, services, contacts. etc.

Accepted output type modifiers:

  • /_list : Short list of available objects, depending on the current request context: hosts, services, etc.



16
17
18
19
20
21
22
23
24
# File 'lib/app/routes/get/objects.rb', line 16

get "/_objects" do

  @data = begin
            @output == :list ? @objects.keys : @objects
          rescue NoMethodError
            nil
          end
  nil
end

- (Object) get(/_runtime))

Print out nagira runtime configuration



338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'lib/app.rb', line 338

get "/_runtime" do
  @data = {
    application: self.class,
    version: VERSION,
    runtime: {
      environment: Nagira.settings.environment,
      home: ENV['HOME'],
      user: ENV['LOGNAME'],
      nagiosFiles: $nagios.keys.map {  |x| {  x =>  $nagios[x].path }}
    }
  }
  nil
end

- (Object) get(/))

Returns application information: name, version, github repository.



356
357
358
359
360
361
362
363
364
# File 'lib/app.rb', line 356

get "/" do
  @data = {
    :application => self.class,
    :version => VERSION,
    :source => GITHUB,
    :apiUrl => request.url.sub(/\/$/,'') + "/_api",
  }
  nil
end

- (Object) get_status

Return all hosts status.

If no output modifier provided, outputs full hosttatus information for each host. Not including services information. When _full modifier is provided data include hoststatus, servicestatus and all comments (servicecomments and hostcomments) for hosts.

Alias: get /_status is the same thing as get /_status/_hosts with ActiveResource compatibility, i.e. for */_hosts request Nagira returns array instead of hash.

Accepted output type modifiers:

  • /_state - Instead of full status information send only current state. For hosts up/down, for services OK, Warn, Critical, Unknown (0,1,2-1)

  • /_list : Short list of available objects, depending on the current request context: hosts, services, etc.

  • /_full - Show full status information. When used in /_status/_full call will display full hoststaus and servicestatus information for each host.

Support for (see API):

  • plural resources: N/A

  • object access by ID: N/A



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/app/routes/get/status.rb', line 137

get /^\/_status(\/_hosts)?$/ do

  @data = @status.dup

  case @output 
  when :state
    @data.each { |k,v| @data[k] = v['hoststatus'].slice("host_name", "current_state") }
  when :list
    @data = @data.keys
  when :full
    @data
  else
    @data.each { |k,v| @data[k] = v['hoststatus'] }
  end

  nil
end

- (Object) get_status_hostname

Hoststatus for single host

Accepted output type modifiers:

  • /_state - Instead of full status information send only current state. For hosts up/down, for services OK, Warn, Critical, Unknown (0,1,2-1)

Support for:

  • plural resources: N/A

  • object access by ID: NO (TODO)

Parameters:

  • :hostname (String)

    Configured Nagios hostname



168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/app/routes/get/status.rb', line 168

get "/_status/:hostname" do |hostname|


  hostname = hostname.to_i if hostname =~ /^\d+$/
  @data =  @status[hostname]['hoststatus'].dup if @status.has_key? hostname
  
  if @output == :state
    @data = @data.slice("host_name", "current_state")
  end

  nil
end

- (Object) get_status_hostname_services

Endpoints:

  • GET /_status/:hostname/_services

  • GET /_status/:hostname/_hostcomments

  • GET /_status/:hostname/_servicecomments

Read services, hostcomments or servicecomments for single host.

Accepted output type modifiers:

  • /_state - Instead of full status information send only current state. For hosts up/down, for services OK, Warn, Critical, Unknown (0,1,2-1)

  • /_list : Short list of available objects, depending on the current request context: hosts, services, etc.

  • /_full - Show full status information. When used in /_status/_full call will display full hoststaus and servicestatus information for each host.

Parameters:

  • :hostname (String)

    Configured Nagios hostname



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/app/routes/get/status.rb', line 90

get %r{^/_status/(?<hostname>[\w\-\.]+)/_(?<service>(services|hostcomments|servicecomments))$} do |hostname,service|

  hostname = hostname.to_i if hostname =~ /^\d+$/
  key = case service
        when 'services' 
          'servicestatus'
        else
          service
        end
          
  if @status && @status[hostname]
    case @output
    when :list
      @data = @status[hostname][key].keys
    when :state
      @data = @status.each { |k,v| @data[k] = v.slice("host_name", "service_description", "current_state") }
    else
      @data = @status[hostname][key]
    end
  end

  nil
end

- (Object) get_status_hostname_services_service_name

Full or short status information for single service on single host.

Accepted output type modifiers:

  • /_state - Instead of full status information send only current state. For hosts up/down, for services OK, Warn, Critical, Unknown (0,1,2-1)

Parameters:

  • :hostname (String)

    Configured Nagios hostname

  • :service_name (String)

    Configured Nagios service for the host



61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/app/routes/get/status.rb', line 61

get "/_status/:hostname/_services/:service_name" do |hostname,service|

  hostname = hostname.to_i if hostname =~ /^\d+$/
  if @status && @status[hostname]
    if @output == :state
      @data = @status[hostname]['servicestatus'][service].slice("hostname", "service_description", "current_state")
    else
      @data = @status[hostname]['servicestatus'][service]
    end
  end
  nil
end

- (Object) after("Object not found or bad request")

If result-set of object/status search is empty return HTTP 404 . This can happen when you are requesting status for not existing host and/or service.



267
268
269
270
271
272
273
274
275
# File 'lib/app.rb', line 267

after do
  if ! @data || @data.empty?
    halt [404, {
            :message => "Object not found or bad request",
            :error => "HTTP::Notfound"
          }.send("to_#{@format}")
         ]
  end
end

- (Array) before("Parse PUT request body")

Process the data before on each HTTP request.

Returns:

  • (Array)

    @input Sets @input instance variable.



10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/app/routes/put.rb', line 10

before do 
  if request.put?
    data = request.body.read
    @input = case @format
            when :json then JSON.parse    data
            when :xml  then Hash.from_xml data
            when :yaml then YAML.load     data
            end
    # Make sure we always return an Array
    @input = [@input] if @input.is_a? Hash
  end
  @input
end

- (Object) before("Parse Nagios files")

Parse nagios files.

Note: *.parse methods are monkey-patched here (if you have required 'lib/nagira' above) to set min parsing interval to avoid file paring on each HTTP request. File is parsed only if it was changed and if it was parsed more then 60 (default) seconds ago. See lib/nagira/timed_parse.rb for mor more info.

In development mode use files located under ./test/data directory. This allows to do development on the host where Nagios is notinstalled. If you want to change this edit configuration in config/environment.rb file.

See also comments in config/default.rb file regarding nagios_cfg, status_cfg, objects_cfg.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/app.rb', line 126

before do

  if Nagira.settings.start_background_parser
    unless $bg.alive?
      logger.warn "Background Parser is configured to run, but is not active"
      $nagios[:config].parse
      $nagios[:status].parse
      $nagios[:objects].parse
    end
    $use_inflight_status  ? @status = $nagios[:status_inflight].status['hosts']
                          : @status = $nagios[:status].status['hosts']
    $use_inflight_objects ? @objects  = $nagios[:objects_inflight].objects
                          : @objects  = $nagios[:objects].objects
  else
    $nagios[:config].parse
    $nagios[:status].parse
    $nagios[:objects].parse
    @status   = $nagios[:status].status['hosts']
    @objects  = $nagios[:objects].objects
  end


##
# TODO: This stuff breaks XML valid. Will have to wait.
#
#     idx = 0
#     @status.keys.uniq.each do |hostname|
#       @status[idx] = @status[hostname]
#       idx += 1
#     end
  #
  # Add plural keys to use ActiveResource with Nagira
  #
  @objects.keys.each do |singular|
    @objects[singular.to_s.pluralize.to_sym] = @objects[singular]
  end

end

- (Object) put("/_host_status/:host_name")

Same as /_status/:host_name (Not implemented)



47
48
49
# File 'lib/app/routes/put/host.rb', line 47

put "/_host_status/:host_name" do 
  "Not implemented: TODO"
end

- (Object) put("/_status")

Submit JSON Hash for multiple services, on multiple hosts.



10
11
12
# File 'lib/app/routes/put/host.rb', line 10

put "/_status" do
  "TODO: Not implemented"
end

- (Object) put(/_status/:host_name/_services/:service_description/_return_code/:return_code/_plugin_output/:plugin_output)

Update single service status on a single host. Use data provided in RESTful path as parameters.

Example

curl  -d "test data"    -X PUT http://localhost:4567/_status/viy/_services/PING/_return_code/0/_plugin_output/OK
 # => ok


93
94
95
96
# File 'lib/app/routes/put/status.rb', line 93

put "/_status/:host_name/_services/:service_description/_return_code/:return_code/_plugin_output/:plugin_output" do 
  @data = update_service_status params
  nil
end

- (Object) put("/_status/:host_name")

Update hoststatus information only for the given host. URL hostname always override hostname given in the JSON file.

Example

$ curl -i -H "Accept: application/json" -d @host.json -X
    PUT http://localhost:4567/_status/svaroh

  => {"result": true, "object": [{"data": {"host_name":"svaroh",
  "status_code": "0", "plugin_output": "ping OK", "action":
  "PROCESS_HOST_CHECK_RESULT"}, "result":true, "messages": []}]}

Example JSON

{
 "status_code":"0",
 "plugin_output" : "ping OK"
}


35
36
37
38
39
40
# File 'lib/app/routes/put/host.rb', line 35

put "/_status/:host_name" do
  @data = update_host_status @input.first.merge({
                                                  'host_name' => params['host_name']
                                                })
  nil
end

- (Object) put("/_status/:host_name/_services")

Update multiple services on the same host.

Hostname from URL always overrides host_name if it's is provided in the JSON data.

Example return JSON data

$ curl -i -H "Accept: application/json" -d @dat_m.json -X PUT
http://localhost:4567/_status/svaroh/_services

  {"result"=>true,
   "object"=>
    [{"data"=>
       {"return_code"=>0,
        "plugin_output"=>"All OK",
        "service_description"=>"PING",
        "host_name"=>"archive",
        "action"=>"PROCESS_SERVICE_CHECK_RESULT"},
      "result"=>true,
      "messages"=>[]}]}

Example JSON for submit

All attributes provided in the example below are requried for host service status information:

  • host_name

  • service_description

  • return_code

  • plugin_output

    [{ "host_name":"viy",
       "service_description":"PING",
       "return_code":"0",
       "plugin_output":"64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.046 ms "
      },
    
    {"host_name":"svaroh",
       "service_description":"Apache",
       "return_code":"2",
       "plugin_output":"HTTP GEt failed"
       }
      ]


54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/app/routes/put/status.rb', line 54

put "/_status/:host_name/_services" do

  data, result = [], true

  @input.each do |datum|
    # FIXME - this calls update for each service. Should be batching them together
    update = update_service_status(
                                    datum.merge({ 
                                                  'host_name' => params['host_name']
                                                })
                                    )
    data << update[:object].first
    result &&= update[:result]
  end
  @data = { result: result, object: data }
  nil

end

- (Object) after("Return formatted data")

If it's a JSON-P request, return its data with prepended @callback function name. JSONP request is detected by before method.

If no callback paramete given, then simply return formatted data as XML, JSON, or YAML in response body.

Example



314
315
316
# File 'lib/app.rb', line 314

after do
  body( @callback ? "#{@callback.to_s} (#{@data.to_json})" : @data.send("to_#{@format}") )
end

- (Object) before("Initial Config")

Do some necessary tasks at start and then run Sinatra app.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/app.rb', line 62

configure do

  $nagios = { }
  $nagios[:config]  = Nagios::Config.new Nagira.settings.nagios_cfg
  $nagios[:config].parse

  $nagios.merge!({
                  status: Nagios::Status.new(  Nagira.settings.status_cfg ||
                                               $nagios[:config].status_file
                                               ),
                  objects: Nagios::Objects.new( Nagira.settings.objects_cfg ||
                                                $nagios[:config].object_cache_file
                                                ),
                  commands: Nagios::ExternalCommands.new( Nagira.settings.command_file ||
                                                          $nagios[:config].command_file
                                                          )
                })

  $nagios.merge!({
                  status_inflight: Nagios::Status.new( Nagira.settings.status_cfg ||
                                                       $nagios[:config].status_file
                                                       ),
                  objects_inflight: Nagios::Objects.new( Nagira.settings.objects_cfg ||
                                                         $nagios[:config].object_cache_file
                                                       )
                }) if ::DEFAULT[:start_background_parser]

  puts "[#{Time.now}] -- Starting Nagira application"
  puts "[#{Time.now}] -- Version #{Nagira::VERSION}"
  puts "[#{Time.now}] -- Running in #{Nagira.settings.environment} environment"
  $nagios.keys.each do |x|
    puts "[#{Time.now}] -- Using nagios #{x} file: #{$nagios[x].path}"
  end

  $nagios[:status].parse
  $nagios[:objects].parse

  @status   = $nagios[:status].status['hosts']
  @objects  = $nagios[:objects].objects

  Nagios::BackgroundParser.new
end

- (Object) before("detect format")

Detect and strip output format extension

Strip extension (@format) from HTTP route and set it as instance variable @format. Valid formats are .xml, .json, .yaml. If format is not specified, it is set to default format (Nagira.settings.format).

@format can be assigned one of the symbols: :xml, :json, :yaml.

Examples

GET /_objects              # => default format
GET /_objects.json         # => :json
GET /_status/_list.yaml    # => :yaml


195
196
197
198
199
# File 'lib/app.rb', line 195

before do
  request.path_info.sub!(/#{settings.format_extensions}/, '')
  @format = ($1 || settings.format).to_sym
  content_type "application/#{@format.to_s}"
end

- (Object) before('detect output mode')

Detect output mode modifier

Detect and strip output type from HTTP route. Full list of output types is :list, :state or :full, corresponding to (+/list, /state, /full routes).

Output type defined by route modifier appended to the end of HTTP route. If no output type specfied it is set to :full. Output mode can be followed by format extension (.json, .xml or .yaml).

Examples

GET /_objects/_list     # => :list
GET /_status/_state     # => :state
GET /_status/:hostname  # => :full
GET /_status            # => :normal


229
230
231
232
# File 'lib/app.rb', line 229

before do
  request.path_info.sub!(/\/_(list|state|full)$/, '')
  @output = ($1 || :normal).to_sym
end

- (Object) update_host_status(params)

Small helper to submit data to ::Nagios::ExternalCommands object. For host status updates sends external command via ::Nagios::ExternalCommands.write method.



49
50
51
# File 'lib/app/routes/put.rb', line 49

def update_host_status params
  put_update :PROCESS_HOST_CHECK_RESULT, params
end

- (Object) update_service_status(params)

Small helper to submit data to ::Nagios::ExternalCommands object. For status updates sends external coond via ::Nagios::ExternalCommands.send method.



42
43
44
# File 'lib/app/routes/put.rb', line 42

def update_service_status params
  put_update :PROCESS_SERVICE_CHECK_RESULT, params
end