sensu-plugins/sensu-plugins-influxdb

View on GitHub
bin/check-influxdb-query.rb

Summary

Maintainability
B
5 hrs
Test Coverage
#! /usr/bin/env ruby
#
#   check-influxdb-query
#
# DESCRIPTION:
#   Check InfluxDB queries
#
# OUTPUT:
#   plain text
#
# PLATFORMS:
#   Linux
#
# DEPENDENCIES:
#   gem: sensu-plugin
#   gem: jsonpath
#   gem: json
#   gem: dentaku
#
# USAGE:
#   example commands
#
# NOTES:
#   See the README here https://github.com/zeroXten/check_influxdb_query
#
# LICENSE:
#   Copyright 2014, Fraser Scott <fraser.scott@gmail.com>
#   Released under the same terms as Sensu (the MIT license); see LICENSE
#   for details.
#

require 'influxdb'
require 'sensu-plugin/check/cli'
require 'json'
require 'jsonpath'
require 'dentaku'

# VERSION = '0.1.0'

#
# Check Influxdb Query
#
class CheckInfluxdbQuery < Sensu::Plugin::Check::CLI
  check_name nil
  option :host,
         short: '-H HOST',
         long: '--host HOST',
         default: 'localhost',
         description: 'InfluxDB host'

  option :port,
         short: '-P PORT',
         long: '--port PORT',
         default: '8086',
         description: 'InfluxDB port'

  option :use_ssl,
         description: 'Turn on/off SSL (default: false)',
         short: '-s',
         long: '--use_ssl',
         boolean: true,
         default: false

  option :verify_ssl,
         description: 'Turn on/off using SSL certificate (default: false)',
         short: '-v',
         long: '--verify_ssl',
         boolean: true,
         default: false

  option :ssl_ca_cert,
         description: 'Path to the ssl ca certificate to connect to the InfluxDB server',
         short: '-C CA_CERT',
         long: '--ssl_ca_cert CA_CERT'

  option :database,
         short: '-d DATABASE',
         long: '--database DATABASE',
         default: 'influxdb',
         description: 'InfluxDB database name'

  option :retry,
         short: '-r RETRY',
         long: '--retry RETRY',
         description: 'InfluxDB retry count with exponential back-off',
         proc: proc(&:to_i),
         default: 12

  option :username,
         short: '-u USERNAME',
         long: '--username USERNAME',
         default: 'root',
         description: 'API username'

  option :password,
         short: '-p PASSWORD',
         long: '--password PASSWORD',
         default: 'root',
         description: 'API password'

  option :query,
         short: '-q QUERY',
         long: '--query QUERY',
         required: true,
         description: 'Query to run. See https://influxdb.com/docs/v0.9/query_language/query_syntax.html'

  option :alias,
         short: '-a ALIAS',
         long: '--alias ALIAS',
         default: nil,
         description: 'Alias of query (e.g. if query and output gets too long)'

  option :mode,
         short: '-m MODE',
         long: '--mode MODE',
         default: 'first',
         description: 'How the results are being checked (one of "first", "last", "max", "min", "sum", "avg") when the query returns more than one value',
         in: %w(first last max min sum avg average)

  option :jsonpath,
         short: '-j JSONPATH',
         long: '--jsonpath JSONPATH',
         default: nil,
         description: 'Json path to select value. Takes the first value, or 0 if none. See http://goessner.net/articles/JsonPath/'

  option :noresult,
         short: '-n',
         long: '--noresult',
         boolean: true,
         description: 'Go critical for no result from query'

  option :warning,
         short: '-w WARNING',
         long: '--warning WARNING',
         default: nil,
         description: "Warning threshold expression. E.g. 'value >= 10'. See https://github.com/rubysolo/dentaku"

  option :critical,
         short: '-c CRITICAL',
         long: '--critical CRITICAL',
         default: nil,
         description: "Critical threshold expression. E.g. 'value >= 20'. See https://github.com/rubysolo/dentaku"

  option :help,
         short: '-h',
         long: '--help',
         description: 'Show this message',
         on: :tail,
         boolean: true,
         show_options: true,
         exit: 0

  # option :version,
  #        short: '-v',
  #        long: '--version',
  #        description: 'Show version',
  #        on: :tail,
  #        boolean: true,
  #        proc: proc { puts "Version #{VERSION}" },
  #        exit: 0

  def run
    influxdb = InfluxDB::Client.new config[:database],
                                    host: config[:host],
                                    port: config[:port],
                                    use_ssl: config[:use_ssl],
                                    verify_ssl: config[:verify_ssl],
                                    ssl_ca_cert: config[:ssl_ca_cert],
                                    retry: config[:retry],
                                    username: config[:username],
                                    password: config[:password]

    value = influxdb.query config[:query]

    query_name = if config[:alias]
                   config[:alias]
                 else
                   config[:query]
                 end

    if config[:noresult] && value.empty?
      critical "No result for query '#{query_name}'"
    elsif config[:noresult] && !config[:jsonpath] && !value.empty?
      ok "Value returned for query '#{query_name}'"
    elsif !config[:noresult] && config[:jsonpath] && value.empty?
      ok 'No result for the query'
    end

    if config[:jsonpath]
      json_path = JsonPath.new(config[:jsonpath])
      calc = Dentaku::Calculator.new
      if config[:mode] == 'any' && json_path.on(value).length >= 1
        json_path.on(value).each do |hashval|
          if config[:critical] && calc.evaluate(config[:critical], value: hashval)
            critical "Value '#{value}' matched '#{config[:critical]}' for query '#{query_name}'"
          elsif config[:warning] && calc.evaluate(config[:warning], value: hashval)
            warning "Value '#{value}' matched '#{config[:warning]}' for query '#{query_name}'"
          end
        end
        ok 'All values OK!'
      else
        value = get_single_value(json_path, value)
        if config[:critical] && calc.evaluate(config[:critical], value: value)
          critical "Value '#{value}' matched '#{config[:critical]}' for query '#{query_name}'"
        elsif config[:warning] && calc.evaluate(config[:warning], value: value)
          warning "Value '#{value}' matched '#{config[:warning]}' for query '#{query_name}'"
        else
          ok "Value '#{value}' ok for query '#{query_name}'"
        end
      end
    else
      puts 'Debug output. Use -j to check value...'
      puts JSON.pretty_generate(value)
    end
  end

  def get_single_value(jpath, value)
    return 0 if jpath.on(value).empty?
    case config[:mode]
    when 'last'
      jpath.on(value).last
    when 'min'
      jpath.on(value).min
    when 'max'
      jpath.on(value).max
    when 'sum'
      jpath.on(value).inject(:+)
    when 'avg', 'average'
      jpath.on(value).inject(:+).to_f / jpath.on(value).length
    when 'first'
      jpath.on(value).first
    end
  end
end