Class: LogStash::Outputs::Icinga

Inherits:
Base
  • Object
show all
Defined in:
lib/logstash/outputs/icinga.rb

Overview

This plugin runs actions on an Icinga server by calling its API. The Icinga API is available since version 2.4. It replaces the formerly used command pipe by providing a similiar interface with filter capabilities. Actions are used in order to process check results, manage downtimes, tell Icinga to send notifications and so on.

This plugin handles a defined set of actions. A list of all Icinga actions is avaiable in the docs.icinga.com/icinga2/latest/doc/module/icinga2/chapter/icinga2-api#icinga2-api-actions[Icinga Docs].

Examples:

. Process a check result based on syslog severity

source,ruby

filter {

if [syslog_severity] == "error" {
  mutate {
    replace => { "exit_status" => "2" }
  }
}

} output

icinga {
  host           => 'demo.icinga.com'
  user           => 'icinga'
  password       => 'supersecret'
  action         => 'process-check-result'
  action_config  => {
    exit_status   => "%{exit_status"
    plugin_output => "%message"
  }
  icinga_host    => "%hostname"
  icinga_service => "dummy"
}

}

. Set a downtime of 2 hours, starting from now

source,ruby

filter {

ruby { code => "event.set('start_time', Time.now.to_i)" }
ruby { code => "event.set('end_time', Time.now.to_i + 7200)" }

} output {

icinga {
  host           => 'demo'
  user           => 'root'
  password       => 'icinga'
  ssl_verify     => false
  action         => 'schedule-downtime'
  action_config  => {
    author     => "logstash"
    comment    => "Downtime set by Logstash Output"
    start_time => "%{start_time}"
    end_time   => "%{end_time}"
  }
  icinga_host    => '%{hostname}'
  icinga_service => 'dummy'
}

Constant Summary collapse

ACTION_CONFIG_FIELDS =
{
    'process-check-result' => {
        'exit_status' => { 'required' => true },
        'plugin_output' => { 'required' => true },
        'performance_data' => {},
        'check_command' => {},
        'check_source' => {}
    },
    'send-custom-notification' => {
        'author' => { 'required' => true },
        'comment' => { 'required' => true },
        'force' => {}
    },
    'add-comment' => {
        'author' => { 'required' => true },
        'comment' => { 'required' => true }
    },
    'remove-comment' => {
        'author' => { 'required' => true }
    },
    'schedule-downtime' => {
        'author' => { 'required' => true },
        'comment' => { 'required' => true},
        'start_time' => { 'required' => true},
        'end_time' => { 'required' => true },
        'duration' => {},
        'fixed' => {},
        'trigger_name' => {},
        'child_options' => {}
    },
    'remove-downtime' => {
        'author' => { 'required' => true }
    }
}

Instance Method Summary collapse

Instance Method Details

#receive(event) ⇒ Object



290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/logstash/outputs/icinga.rb', line 290

def receive(event)

  @available_hosts = @host.count

  begin
    @httpclient ||= connect
    request_body = Hash.new
    icinga_host = event.sprintf(@icinga_host)
    icinga_service = event.sprintf(@icinga_service)

    @uri.path = "/v1/actions/#{@action}"

    # Depending on the action we take, set either a filter in the request body or set a host and/or service in the
    # url parameters.
    case @action
      when 'remove-downtime', 'remove-comment'
        action_type = @action.split('-').last
        request_body['type'] = action_type.capitalize
        if @icinga_service
          request_body['filter'] = "host.name == \"#{icinga_host}\" && service.name == \"#{icinga_service}\" && #{action_type}.author == \"#{@action_config['author']}\""
        else
          request_body['filter'] = "host.name == \"#{icinga_host}\" && #{action_type}.author == \"#{@action_config['author']}\""
        end
      else
        if @icinga_service
          @uri.query = URI.encode_www_form({:service => "#{icinga_host}!#{icinga_service}"})
        else
          @uri.query = URI.encode_www_form({:host => icinga_host})
        end

        @action_config.each do |key, value|
          request_body[key] = event.sprintf(value)
        end
    end

    request = Net::HTTP::Post.new(@uri.request_uri)
    request.initialize_http_header({'Accept' => 'application/json'})
    request.basic_auth(@user, @password.value)
    request.body = LogStash::Json.dump(request_body)

    response = @httpclient.request(request)
    raise StandardError if response.code != '200'

    response_body = LogStash::Json.load(response.body)
    response_body['results'].each do |result|
      logging_data = {
          :host => "#{@uri.host}:#{@uri.port}",
          :request_path => request.path,
          :request_body => request.body,
          :result_code => result['code'].to_i,
          :result_status => result['status']
      }

      if result['code'] == 200
        @logger.debug("Action '#{@action}' succeeded", logging_data)
      else
        @logger.warn("Action '#{@action}' failed", logging_data)
      end
    end.empty? and begin
      @logger.debug('Returned result was epty', :response_body => response.body)
    end

  rescue Timeout::Error => e
    @logger.warn( "Request failed",
                  :host => @uri.host, :port => @uri.port,
                  :path => request.path, :body => request.body,
                  :error => e )
    # If a host is not reachable, try the same request with the next host in the list. Try each host host only once per
    # request.
    if not (@available_hosts -= 1).zero?
      @httpclient = connect
      @logger.info("Retrying request with '#{@uri.host}:#{@uri.port}'")
      retry
    end

  rescue Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
      Net::ProtocolError, OpenSSL::SSL::SSLError, StandardError => e
    @logger.warn("Request failed",
                   :host => @uri.host, :port => @uri.port,
                   :path => request.path, :body => request.body,
                   :error => e )

    if response
      @logger.warn("Response: ", :response_code => response.code, :response_body => response.body)

      if response.code == '404' && @create_object == true
        object = create_object(event)

        if object.code == '200'
          @logger.info("Retrying action on freshly created object", :action => @action)
          retry
        else
          @logger.warn("Failed to create object", :response_code => object.code, :response_body => object.body)
        end
      end
    end


  end
end

#registerObject



283
284
285
286
287
# File 'lib/logstash/outputs/icinga.rb', line 283

def register
  validate_action_config
  @ssl_verify ? @ssl_verify_mode = OpenSSL::SSL::VERIFY_PEER : @ssl_verify_mode = OpenSSL::SSL::VERIFY_NONE
  @host_id = 0
end