Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a check that supports xml #8

Open
ChuckC90 opened this issue Jul 22, 2015 · 1 comment
Open

Create a check that supports xml #8

ChuckC90 opened this issue Jul 22, 2015 · 1 comment

Comments

@ChuckC90
Copy link

ChuckC90 commented Jul 22, 2015

We found the check-http-json.rb was a very helpful check for our JSON returning API calls but wanted to be able to leverage it for our XML returning API calls as well.

So I have made some tweaks (as per code below) to allow you to select response type XML or JSON, then it converts the XML response to JSON and performs the same JSON search on it.
This has been working well for us so far.

Also, in order to be able to output this to influxDB to be able to build a dashboard in grafana I added the ability to select an output type or Check or Metric that will output in the Check output format or metric output format so that it will go into influxdb.

Just sharing this back in case you wanted to consider making this part of the plugin or making your own modification to fit this need.

# ! /usr/bin/env ruby
# 
# check-http-json
# 
# DESCRIPTION:
# Takes either a URL or a combination of host/path/query/port/ssl, and checks
# for valid JSON output in the response. Can also optionally validate simple
# string key/value pairs.
# 
# OUTPUT:
# plain text
# 
# PLATFORMS:
# Linux
# 
# DEPENDENCIES:
# gem: sensu-plugin
# gem: json
# 
# USAGE:
# #YELLOW
# 
# NOTES:
# Based on Check HTTP by Sonian Inc.
# Charles Curran: Made small tweak to allow for XML response use, converts to JSON then does the KEY = VALUE compare
# 
# LICENSE:
# Copyright 2013 Matt Revell [email protected]
# Released under the same terms as Sensu (the MIT license); see LICENSE
# for details.
# 

require 'sensu-plugin/check/cli'
require 'json'
require 'net/http'
require 'net/https'
require 'active_support/core_ext/hash'
# 
# Check JSON
# 

class CheckJson < Sensu::Plugin::Check::CLI
  option :url, short: '-u URL'
  option :host, short: '-h HOST'
  option :path, short: '-p PATH'
  option :query, short: '-q QUERY'
  option :port, short: '-P PORT', proc: proc(&:to_i)
  option :header, short: '-H HEADER', long: '--header HEADER'
  option :ssl, short: '-s', boolean: true, default: false
  option :insecure, short: '-k', boolean: true, default: false
  option :user, short: '-U', long: '--username USER'
  option :password, short: '-a', long: '--password PASS'
  option :cert, short: '-c FILE'
  option :cacert, short: '-C FILE'
  option :timeout, short: '-t SECS', proc: proc(&:to_i), default: 15
  option :key, short: '-K KEY', long: '--key KEY'
  option :value, short: '-v VALUE', long: '--value VALUE'
  ##CHUCK ADDED OPTIONS
  option :responseType, short: '-r RESPONSETYPE', default:'JSON'
  option :outputType, short: '-o OUTPUTTYPE', default:'CHECK'
  option :metricscheme, short: '-m METRICSCHEME'

  ##CHUCK ADDED FUNCTION
  def output_result(output,metircscheme, status, outputMessage)
    if output.upcase == "CHECK"
        if status == "OK"
            ok outputMessage
        elsif status == "WARNING"
            warning outputMessage
        elsif status == "FAIL"
            fail outputMessage
        elsif status == "CRITICAL"
            critical outputMessage
        else #UKNOWN
            unknown outputMessage
        end
    end
    if output.upcase == "METRIC"
        if status == "OK"
            state = 0
        elsif status == "WARNING"
            state = 1
        elsif status == "FAIL"
            state = 2
        elsif status == "CRITICAL"
            state = 2
        else #UNKNOWN
            state = 3
        end
       puts "#{metircscheme}\t#{state}\t#{Time.now}"
       exit 0
    end
  end

  def run
    if config[:url]
      uri = URI.parse(config[:url])
      config[:host] = uri.host
      config[:path] = uri.path
      config[:query] = uri.query
      config[:port] = uri.port
      config[:ssl] = uri.scheme == 'https'
    else
      # #YELLOW
      unless config[:host] && config[:path] # rubocop:disable IfUnlessModifier
        # unknown 'No URL specified'
        #CHUCK
        output_result(config[:outputType],config[:metricscheme], "UKNOWN", "No URL specified")
      end
      config[:port] ||= config[:ssl] ? 443 : 80
    end
    begin
      timeout(config[:timeout]) do
        acquire_resource
      end
    rescue Timeout::Error
      # critical 'Connection timed out'
      #CHUCK
      output_result(config[:outputType],config[:metricscheme], "CRITICAL", "Connection timed out")
    rescue => e
      # critical "Connection error: #{e.message}"
      #CHUCK
      output_result(config[:outputType],config[:metricscheme], "CRITICAL", "Connection error: #{e.message}")
    end
  end

  def json_valid?(str)
    JSON.parse(str)
    return true
  rescue JSON::ParserError
    return false
  end

  def acquire_resource # rubocop:disable all
    http = Net::HTTP.new(config[:host], config[:port])


if config[:ssl]
  http.use_ssl = true
  if config[:cert]
    cert_data = File.read(config[:cert])
    http.cert = OpenSSL::X509::Certificate.new(cert_data)
    http.key = OpenSSL::PKey::RSA.new(cert_data, nil)
  end
  http.ca_file = config[:cacert] if config[:cacert]
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config[:insecure]
end

req = Net::HTTP::Get.new([config[:path], config[:query]].compact.join('?'))
if !config[:user].nil? && !config[:password].nil?
  req.basic_auth config[:user], config[:password]
end
if config[:header]
  config[:header].split(',').each do |header|
    h, v = header.split(':', 2)
    req[h] = v.strip
  end
end
res = http.request(req)

##Chuck: Added Check for New Response Type to Convert XML Response to JSON so it can process it
if config[:responseType] == "XML" then
    responseBody = Hash.from_xml(res.body).to_json
    # puts "XML Converted To : #{responseBody}"
elsif config[:responseType] == "JSON" then
    responseBody = res.body
else
    # critical ' is an invalid response type from request'
    ##CHUCK
    output_result(config[:outputType],config[:metricscheme], "CRITICAL", ' is an invalid response type from request')
end

# critical res.code unless res.code =~ /^2/
# critical 'invalid JSON from request' unless json_valid?(responseBody)
# ok 'valid JSON returned' if config[:key].nil? && config[:value].nil?

##CHUCK
if !res.code =~ /^2/
    output_result(config[:outputType],config[:metricscheme], "CRITICAL", res.code)
end
if !json_valid?(responseBody)
    output_result(config[:outputType],config[:metricscheme], "CRITICAL", " invalid JSON from request")
end
if config[:key].nil? && config[:value].nil?
    output_result(config[:outputType],config[:metricscheme], "OK", " valid JSON returned")
end

json = JSON.parse(responseBody)

begin
  keys = config[:key].scan(/(?:\\\.|[^.])+/).map { |key| key.gsub(/\\./, '.') }

  leaf = keys.reduce(json) do |tree, key|
    # fail "could not find key: #{config[:key]}" unless tree.key?(key)
    ##CHUCK
    if !tree.key?(key)
        output_result(config[:outputType],config[:metricscheme], "FAIL",  "could not find key: #{config[:key]}")
    end
    tree[key]
    end

  # fail "unexpected value for key: '#{config[:value]}' != '#{leaf}'" unless leaf == config[:value]
  output_result(config[:outputType],config[:metricscheme], "FAIL'"  "unexpected value for key: '#{config[:value]}' != '#{leaf}'") unless leaf == config[:value]

  # ok "key has expected value: '#{config[:key]}' = '#{config[:value]}'"
  output_result(config[:outputType],config[:metricscheme], "OK", "key has expected value: '#{config[:key]}' = '#{config[:value]}'")
rescue => e
  # critical "key check failed: #{e}"
  output_result(config[:outputType],config[:metricscheme], "CRITICAL", " key check failed: #{e}")
end

  end
end
@mattyjones
Copy link
Member

@CharlesCurran

I would be willing to take a PR for this but this functionality would need to be another check.

The check-http-json should not be checking xml, the name would be misleading ;) If you want to create a new check, check-http-xml, that would be best but we could also create a check called, check-http-response, that could do both.

I would not rename the current check, as that would break versioning rules.

@majormoses majormoses changed the title Had to expand check-http-json.rb to take in XML and Provide Metric Output Create a check that supports xml May 18, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants