Created
June 7, 2012 20:02
-
-
Save rogthefrog/2891235 to your computer and use it in GitHub Desktop.
Nagios plugin for Flash Media Server monitoring
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 [email protected]_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 [email protected]_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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample yaml config file available at https://gist.github.com/3363133