Class: Nagira

Inherits:
Sinatra::Base
  • Object
show all
Defined in:
app/app.rb,
app/put.rb,
app/put/host.rb,
app/get/config.rb,
app/put/status.rb,
app/get/status.rb,
app/get/objects.rb,
app/get/status/hostgroups.rb,
app/get/status/servicegroups.rb,
lib/nagira.rb,
lib/nagira/hostgroup.rb,
lib/nagira/hostservice.rb,
lib/nagira/servicegroup.rb

Overview

PUT method routes for services status.

Defined Under Namespace

Classes: HostService, Hostgroup, Servicegroup

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.



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

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.



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

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.



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

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

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



294
295
296
# File 'app/app.rb', line 294

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.



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

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 'app/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



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

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 'app/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 'app/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 'app/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



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

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.



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

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



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'app/get/status.rb', line 141

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_hostgroup

Return full status of the hostgroup: including host status and service status.



17
18
19
20
# File 'app/get/status/hostgroups.rb', line 17

get "/_status/_hostgroup/:hostgroup" do |hostgroup|
  @data = Hostgroup.new(hostgroup).full
  nil
end

- (Object) get("/_status/_hostgroup/:hostgroup/_host")

Endpoint:

  • GET /_status/_hostgroup/:host



39
40
41
42
# File 'app/get/status/hostgroups.rb', line 39

get "/_status/_hostgroup/:hostgroup/_host" do |hostgroup|
  @data = Hostgroup.new(hostgroup).host_status
  nil
end

- (Object) get_status_hostgroup_service

Endpoint:

  • GET /_status/_hostgroup/:hostgroup/_service



28
29
30
31
# File 'app/get/status/hostgroups.rb', line 28

get "/_status/_hostgroup/:hostgroup/_service" do |hostgroup|
  @data = Hostgroup.new(hostgroup).service_status
  nil
end

- (Object) get_status_hostname

Hoststatus for single host

Endpoint

  • get “/_status/:hostname”

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



176
177
178
179
180
181
182
183
184
185
186
187
# File 'app/get/status.rb', line 176

get %r{^/_status/(?<hostname>#{hostname_regex})$} 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



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'app/get/status.rb', line 94

get %r{^/_status/(?<hostname>#{hostname_regex})/_(?<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



65
66
67
68
69
70
71
72
73
74
75
76
# File 'app/get/status.rb', line 65

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) get_status_servicegroup

Endpoint:

  • GET /_status/_servicegroup/:servicegroup

Supported extensions: _state, _list



20
21
22
23
24
25
26
# File 'app/get/status/servicegroups.rb', line 20

get "/_status/_servicegroup/:servicegroup" do |group_name|

  servicegroup = Servicegroup.new(group_name)

  @data = servicegroup.send(@output)
  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.



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

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 'app/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.



125
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
# File 'app/app.rb', line 125

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 'app/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 'app/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 'app/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 'app/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 'app/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

$ curl  'http://localhost:4567/?callback=test'
    test(["{\"application\":\"Nagira\",\"version\":\"0.1.3\",\"url\":\"http://dmytro.github.com/nagira/\"}"])


313
314
315
# File 'app/app.rb', line 313

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.



61
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
# File 'app/app.rb', line 61

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


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

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


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

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 'app/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 'app/put.rb', line 42

def update_service_status params
  put_update :PROCESS_SERVICE_CHECK_RESULT, params
end