Last active
August 29, 2015 14:02
-
-
Save cinsk/f8375d0b0169bacabc54 to your computer and use it in GitHub Desktop.
Check the status of the processes (# of processes, # of fds, memory usage, etc.) in Nagios format
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/env ruby | |
require 'timeout' | |
require 'getoptlong' | |
RLIMIT_MAP = { | |
"Max cpu time" => Process::RLIMIT_CPU, | |
"Max file size" => Process::RLIMIT_FSIZE, | |
"Max data size" => Process::RLIMIT_DATA, | |
"Max stack size" => Process::RLIMIT_STACK, | |
"Max core file size" => Process::RLIMIT_CORE, | |
"Max resident set" => Process::RLIMIT_RSS, | |
"Max processes" => Process::RLIMIT_NPROC, | |
"Max open files" => Process::RLIMIT_NOFILE, | |
"Max locked memory" => Process::RLIMIT_MEMLOCK, | |
"Max address space" => Process::RLIMIT_AS, | |
} | |
fdopen_safe_percent = nil | |
mem_safe_percent = nil | |
def rlimits(pid = nil) | |
pid = Process.pid unless pid | |
lims = {} | |
IO.foreach("/proc/#{pid}/limits") do |line| | |
fields = line.chomp.split(/[[:space:]][[:space:]]+/) | |
key = RLIMIT_MAP[fields[0]] | |
if key | |
soft = fields[1] | |
hard = fields[2] | |
soft = (soft == "unlimited") ? Process::RLIM_INFINITY : soft.to_i | |
hard = (hard == "unlimited") ? Process::RLIM_INFINITY : hard.to_i | |
lims[key] = [soft, hard] | |
end | |
end | |
lims | |
rescue | |
nil | |
end | |
def ps_by_command(cmdname) | |
ps = {} | |
Timeout.timeout(1) do | |
IO.popen("ps -C #{cmdname} -o pid=,etime=,nlwp=,%mem=") do |io| | |
while line = io.gets | |
toks = line.strip.chomp.split(/ +/) | |
continue if toks.length != 4 | |
p = {} | |
pid = toks[0].to_i | |
p[:pid] = pid | |
p[:elapsed] = toks[1] | |
p[:nlwp] = toks[2].to_i | |
p[:mem] = toks[3].to_f | |
ps[pid] = p | |
end | |
end | |
end | |
ps | |
rescue Timeout::Error => ex | |
puts "timeout: #{ex}" | |
ps | |
end | |
def proc_fd_info(pid = nil) | |
pid = Process.pid unless pid | |
fds = 0 | |
Timeout.timeout(1) do | |
IO.popen("ls /proc/#{pid}/fd | wc -l") do |io| | |
fds = io.gets.to_i | |
end | |
end | |
limits = rlimits(pid) | |
[ fds, limits[Process::RLIMIT_NOFILE][0] ] | |
end | |
class Result | |
RESULT_TYPES = [ :OK, :WARNING, :CRITICAL ] | |
def initialize(result = :OK) | |
@result = result | |
end | |
def get() | |
@result | |
end | |
def reset() | |
@result = :OK | |
end | |
def set(result) | |
idx_new = RESULT_TYPES.find_index(result) | |
idx_old = RESULT_TYPES.find_index(@result) | |
@result = result if idx_new > idx_old | |
end | |
def to_s | |
return @result.to_s | |
end | |
end | |
def help_and_exit | |
msg = <<EOF | |
Usage: #{File.basename($PROGRAM_NAME)} [OPTION...] COMMAND_NAME | |
-n, --expected=N Set the expected number of processes to N | |
-f, --fd=PERCENTAGE Set the state to WARNING if the open FDs percent | |
is equal to or greater than PERCENTAGE | |
-m, --mem=PERCENTAGE Set the state to WARNING if the memory usage | |
is greater than PERCENTAGE | |
-c, --classname NAME Set the name of the class to NAME. | |
Uppercase of COMMAND_NAME is used by default. | |
-l, --filelock FILE If FILE does not exist, do nothing but report ok. | |
--help Show help messages and exit | |
This program will examine the system for the process started with | |
COMMAND_NAME. It reports in Nagios plugin format; OK iff the number | |
of processes meets the condition provided by '-n' option. Otherwise | |
it reports as either WARNING or CRITICAL. | |
PERCENTAGE should be a floating point value between 0 and 1. | |
Send a bug report to <cinsk dot shin at samsung dot com> | |
EOF | |
puts msg | |
exit 0 | |
end | |
result = Result.new | |
class_name = nil | |
num_expected = nil | |
predicate_file = nil | |
begin | |
options = | |
GetoptLong.new( | |
[ '--help', GetoptLong::NO_ARGUMENT ], | |
[ '--nexpected', '-n', GetoptLong::REQUIRED_ARGUMENT ], | |
[ '--fd', '-f', GetoptLong::REQUIRED_ARGUMENT ], | |
[ '--mem', '-m', GetoptLong::REQUIRED_ARGUMENT ], | |
[ '--filelock', '-l', GetoptLong::REQUIRED_ARGUMENT ], | |
[ '--classname', '-c', GetoptLong::REQUIRED_ARGUMENT ]) | |
options.each do |opt, arg| | |
case opt | |
when '--nexpected' | |
if arg.to_i < 1 | |
STDERR.write("#{File.basename($PROGRAM_NAME)}: invalid value for `-n'\n") | |
exit 1 | |
end | |
num_expected = arg.to_i | |
when '--fd' then fdopen_safe_percent = arg.to_f | |
when '--mem' then mem_safe_percent = arg.to_f | |
when '--classname' then class_name = arg | |
when '--filelock' then predicate_file = arg | |
when '--help' then help_and_exit | |
end | |
end | |
rescue => ex | |
puts "ex: #{ex}" | |
STDERR.write("#{File.basename($PROGRAM_NAME)}: Try `--help' for help\n") | |
exit 1 | |
end | |
# puts "argv: #{ARGV}" | |
cmd_name = ARGV.shift | |
unless cmd_name | |
STDERR.write("#{File.basename($PROGRAM_NAME)}: argument required\n"); | |
exit 1 | |
end | |
class_name = cmd_name.upcase unless class_name | |
procs = {} | |
while cmd_name | |
procs.merge! ps_by_command(cmd_name) | |
cmd_name = ARGV.shift | |
end | |
if num_expected | |
result.set(:CRITICAL) if procs.length < num_expected | |
result.set(:WARNING) if procs.length > num_expected | |
end | |
procs.each_value do |info| | |
nopen = proc_fd_info info[:pid] | |
info[:fds] = nopen | |
result.set(:WARNING) if fdopen_safe_percent and nopen[0].to_f / nopen[1] >= fdopen_safe_percent | |
result.set(:WARNING) if mem_safe_percent and info[:mem] >= mem_safe_percent | |
end | |
expected = num_expected | |
expected = "??" unless num_expected | |
result.reset if predicate_file and File.exists?(predicate_file) | |
print "#{class_name} #{result} - #{procs.length}/#{expected} process(es); | " | |
#puts " PID NLWP MEM ELAPSED FDS" | |
procs.each_value do |info| | |
fdinfo = if info[:fds][1] == Process::RLIM_INFINITY | |
"#{info[:fds][0]}/unlimited" | |
else | |
"#{info[:fds][0]}/#{info[:fds][1]}" | |
end | |
puts "PID=#{info[:pid].to_s};NLWP=#{info[:nlwp].to_s};MEM=#{info[:mem].to_s};ELAPSED=#{info[:elapsed]};FDS=#{fdinfo};" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment