sensu-plugins/sensu-plugins-victorops

View on GitHub
bin/handler-victorops.rb

Summary

Maintainability
C
1 day
Test Coverage
#!/usr/bin/env ruby
# frozen_string_literal: true

# This handler creates and resolves victorops incidents
#
# Released under the same terms as Sensu (the MIT license); see LICENSE
# for details.
#
# arguments:
#   - settingsname: Sensu settings name, defaults to victorops
#   - routingkey: VictorOps routing key
#
# NOTE: This plugin is Sensu Go enabled. To use this plugin with Sensu Go, add --map_go_event_into_ruby as part of the command.
#  Example:
#    handler-victorops.rb --map_go_event_into_ruby

require 'sensu-handler'
require 'uri'
require 'net/http'
require 'net/https'
require 'json'

class VictorOps < Sensu::Handler
  option :api_url,
         description: 'VictorOps API URL without routing key. Or use envvar VICTOROPS_API_URL.',
         short: '-a APIURL',
         long: '--api-url APIURL',
         default: nil

  option :settingsname,
         description: 'Sensu settings name',
         short: '-n NAME',
         long: '--name NAME',
         default: 'victorops'

  option :routing_key,
         description: 'Routing key. Or use envvar VICTOROPS_ROUTING_KEY.',
         short: '-r KEY',
         long: '--routingkey KEY',
         default: nil

  option :dryrun,
         description: 'Report settings, and take no action',
         long: '--dryrun',
         boolean: true,
         default: false

  def set_api_url
    # validate that we have an api url - environment variables take precedence
    api_url = ENV['VICTOROPS_API_URL']
    api_url = config[:api_url] if api_url.nil?
    if api_url.nil?
      # validate that we have a settings name
      if defined?(settings[config[:settingsname]]) && !settings[config[:settingsname]].nil?
        api_url = settings[config[:settingsname]]['api_url']
      else
        raise "victorops.rb sensu setting '#{config[:settingsname]}' not found or empty" unless config[:dryrun]
      end
    end
    return api_url if defined?(api_url) && !api_url.nil?

    raise "victorops.rb sensu setting '#{config[:settingsname]}.api_url' not found or empty" unless config[:dryrun]
  end

  def set_routing_key
    # validate that we have a routing key - environment variables take precedence
    routing_key = ENV['VICTOROPS_ROUTING_KEY']
    routing_key = config[:routing_key] if routing_key.nil?
    if routing_key.nil?
      # validate that we have a settings name in the config
      if defined?(settings[config[:settingsname]]) && !settings[config[:settingsname]].nil?
        routing_key = settings[config[:settingsname]]['routing_key']
      else
        raise "victorops.rb sensu setting '#{config[:settingsname]}' not found or empty" unless config[:dryrun]
      end
    end
    return routing_key if defined?(routing_key) && !routing_key.nil?

    raise 'routing key not defined, should be in Sensu settings or passed via command arguments' unless config[:dryrun]
  end

  def dry_run(api_url, routing_key, uri, payload)
    return unless config[:dryrun]

    puts 'Dryrun: reporting settings and exiting'
    puts "  option settingsname set: #{config[:settingsname]}"
    puts 'Determing API_URL to use:'
    if ENV['VICTOROPS_API_URL']
      puts "  envvar VICTOROPS_API_URL set: #{ENV['VICTOROPS_API_URL']}"
    else
      puts '  envvar VICTOROPS_API_URL not set'
    end
    if config[:api_url]
      puts "  option api-url set: #{config[:api_url]}"
    else
      puts '  option api-url not set'
    end
    if settings[config[:settingsname]]
      puts "  settings api_url set: #{settings[config[:settingsname]]['api_url']}" if settings[config[:settingsname]]['api_url']
    else
      puts "  setting name: #{config[:settingsname]} does not exist"
    end
    puts "  using: #{api_url}"
    puts 'Determing ROUTING_KEY to use:'
    if ENV['VICTOROPS_ROUTING_KEY']
      puts "  envvar VICTOROPS_ROUTING_KEY set: #{ENV['VICTOROPS_ROUTING_KEY']}"
    else
      puts '  envvar VICTOROPS_ROUTING_KEY not set'
    end
    if config[:routing_key]
      puts "  option routingkey set: #{config[:routing_key]}"
    else
      puts '  option routingkey not set'
    end
    if settings[config[:settingsname]]
      puts "  settings routing_key set: #{settings[config[:settingsname]]['routing_key']}" if settings[config[:settingsname]]['routing_key']
    else
      puts "  setting name: #{config[:settingsname]} does not exist"
    end
    puts "  using: #{routing_key}"
    puts " #{@event['action']}"
    puts "VictorOps Message URI: #{uri}"
    puts "  Message type: #{payload[:message_type]}"
  end

  def handle
    # validate that we have a client defined
    unless defined?(@event['client']) && !@event['client']['name'].nil?
      raise 'victorops.rb sensu client not found or has no name. If using with Sensu Go please ensure you have enabled event mapping'
    end

    api_url = set_api_url
    routing_key = set_routing_key

    incident_key = @event['client']['name'] + '/' + @event['check']['name']

    description = @event['check']['notification']
    description ||= [@event['client']['name'], @event['check']['name'], @event['check']['output']].join(' : ')
    host = @event['client']['name']
    entity_id = incident_key
    state_message = description
    case @event['action']
    when 'create'
      message_type = case @event['check']['status']
                     when 1
                       'WARNING'
                     else
                       'CRITICAL'
                     end
    when 'resolve'
      message_type = 'RECOVERY'
    end
    payload = {}
    payload[:message_type] = message_type
    payload[:state_message] = state_message.chomp
    payload[:entity_id] = entity_id
    payload[:host_name] = host
    payload[:monitoring_tool] = 'sensu'

    # Add in client data
    payload[:check] = @event['check']
    payload[:client] = @event['client']

    uri   = URI("#{api_url.chomp('/')}/#{routing_key}")
    https = Net::HTTP.new(uri.host, uri.port)

    https.use_ssl = true
    if config[:dryrun]
      dry_run(api_url, routing_key, uri, payload)
      return
    end

    begin
      Timeout.timeout(10) do
        request      = Net::HTTP::Post.new(uri.path)
        request.body = payload.to_json
        response     = https.request(request)

        if response.code == '200'
          puts "victorops -- #{@event['action'].capitalize}'d incident -- #{incident_key}"
        else
          puts "victorops -- failed to #{@event['action']} incident -- #{incident_key}"
          puts "victorops -- response: #{response.inspect}"
        end
      end
    rescue Timeout::Error
      puts 'victorops -- timed out while attempting to ' + @event['action'] + ' a incident -- ' + incident_key
    end
  end
end