Skip to content

Instantly share code, notes, and snippets.

@rogthefrog
Created June 7, 2012 20:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rogthefrog/2891235 to your computer and use it in GitHub Desktop.
Save rogthefrog/2891235 to your computer and use it in GitHub Desktop.
Nagios plugin for Flash Media Server monitoring
#!/usr/bin/ruby
# nagios plugin for Flash Media Server monitoring
# roger 2012-06-04
require 'rubygems'
require 'open-uri'
require 'optparse'
require 'ostruct'
require 'set'
require 'xmlsimple'
require 'yaml'
class FMSChecker
VERBOSE = 0
CALL = "ping"
CALLS = [ 'getServerStats', 'getIOStats', 'ping' ].freeze
VERBOSITY = [ 0, 1 ].freeze
CONFIG = "check_fms.yml"
OK = 0
WARNING = 1
CRITICAL = 2
def configure
@config = YAML.load_file(CONFIG)
end
def parse_opts
@options = OpenStruct.new
# set defaults
@options.url = @config["host"]["url"]
@options.port = @config["host"]["port"]
@options.user = @config["host"]["user"]
@options.pass = @config["host"]["pass"]
@options.call = CALL
@options.verbose = VERBOSE
OptionParser.new do |o|
o.on('-u [URL]') { |url| @options.url = url if url }
o.on('-p [PORT]') { |port| @options.port = port if port }
o.on('-s [USER]') { |user| @options.user = user if user }
o.on('-a [PASSWORD]') { |pass| @options.pass = pass if pass }
o.on('-l [API_CALL]') { |call| @options.call = call if call && CALLS.include?(call) }
o.on('-v [VERBOSITY]') { |verb| @options.verbose = verb.to_i if VERBOSITY.include?(verb.to_i) }
o.on('-h') { puts o; Process.exit(false) }
o.parse!
end
verbose_output(@options)
end
def check
@stats = parse_stats(load_stats(@options.call))
# are thresholds set in config?
if !@config.has_key?(@options.call)
log_warning("%s has no parameters set in %s" % [ @options.call, CONFIG ])
return false
end
# delegate the check to a method, if defined
if @config[@options.call].has_key?("method")
method = @config[@options.call]["method"].to_sym
return self.send method
end
# then check all the stats
@config[@options.call].each do |stat, params|
label = params["label"]
section = params["section"]
warning = params["warning"]
critical = params["critical"]
# check min
verbose_output "checking #{stat} in #{section}"
if @stats.has_key?(section) && @stats[section].has_key?(stat)
actual = @stats[section][stat]
w_min = @config[@options.call][stat]["warning"]["min"]
w_max = @config[@options.call][stat]["warning"]["max"]
c_min = @config[@options.call][stat]["critical"]["min"]
c_max = @config[@options.call][stat]["critical"]["max"]
# append the stat to the status message
status_update(stat, actual)
if (c_min > -1 && actual < c_min)
log_error("%s actual %s less than c_min %s" % [ stat, actual, c_min ])
elsif (w_min > -1 && actual < w_min)
log_warning("%s actual %s less than w_min %s" % [ stat, actual, w_min ])
end
if (c_max > -1 && actual > c_max)
log_error("%s actual %s greater than c_max %s" % [ stat, actual, c_max ])
elsif (w_max > -1 && actual > w_max)
log_warning("%s actual %s greater than w_max %s" % [ stat, actual, w_max ])
end
else
log_warning("%s wants me to check stat %s in %s section, but that data point is not available" % [ CONFIG, stat, section ])
end
end
warnings.empty? && errors.empty?
end
def errors
@errors = Set.new if @errors.nil?
@errors
end
def warnings
@warnings = Set.new if @warnings.nil?
@warnings
end
def status
if @status.nil? || @status.empty?
"no stats"
else
@status.join(", ")
end
end
def exit(success)
if success
puts "FMS %s STATUS: OK - %s" % [ @options.call, status ]
Process.exit(true)
else
puts "FMS %s STATUS: ERROR (%s WARNING, %s CRITICAL) - %s" % [ @options.call.upcase, warnings.size, errors.size, status ]
Process.exit(errors.empty? ? WARNING : CRITICAL)
end
end
private
def log_error(error)
verbose_output(error)
errors.add(error)
errors
end
def log_warning(warn)
verbose_output(warn)
warnings.add(warn)
warnings
end
def url(call)
"%s:%s/admin/%s?auser=%s&apswd=%s" % [ @options.url, @options.port, call, @options.user, @options.pass ]
end
def load_stats(call)
open(url(call), :proxy => nil) { |u| u.read }
end
# pulls the XML stats
# and restructures them for ease of access
# output is @stats["stats"]
def parse_stats(xml)
@stats = XmlSimple.xml_in(xml, {"ForceArray" => false})
# restructure a bit
data = nil
io = nil
if @stats.has_key?("data")
data = @stats["data"]
if data.has_key?("io")
io = data["io"]
data.delete("io")
end
end
@stats["data"] = data
@stats["io"] = io
# clean up the numbers
unless @stats["data"].nil?
@stats["data"].each { |k,v| @stats["data"][k] = v.to_i if v.match(/^[0-9.]+$/) }
end
unless @stats["io"].nil?
@stats["io"].each { |k,v| @stats["io"][k] = v.to_i if v.match(/^[0-9.]+$/) }
end
@stats
end
def verbose_output(out)
puts out if @options.verbose > 0
end
# this is used by all the calls
def check_connection
success = true
@errors ||= Set.new
@stats ||= parse_stats(load_stats('ping'))
verbose_output(@stats)
if !@stats.has_key? 'code' || @stats['code'].empty?
log_error "No response code in XML response"
success = false
end
if !@stats['code'].match(/success/i)
log_error "Call failed. Status [%s]" % @stats['code'].first
success = false
end
success
end
def status_update(stat, value)
@status ||= []
@status << "%s = %s" % [ stat, value ]
@status
end
end
c = FMSChecker.new
c.configure
c.parse_opts
c.exit(c.check)
@rogthefrog
Copy link
Author

Sample yaml config file available at https://gist.github.com/3363133

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment