tabin-plugins 0.2.3

Libs for building nagios-compatible check scripts, some scripts, and some libs to read from /proc and /sys on Linux.
extern crate rustc_serialize;

extern crate docopt;
extern crate tabin_plugins;

use docopt::Docopt;

use tabin_plugins::linux::pages_to_human_size;
use tabin_plugins::Status;
use tabin_plugins::procfs::{RunningProcs, MemInfo};

static USAGE: &'static str = "
Usage: check-ram [options]
       check-ram -h | --help

Options:
    -h, --help             Show this help message

    -w, --warn=<percent>   Percent used to warn at      [default: 80]
    -c, --crit=<percent>   Percent used to critical at  [default: 95]

    --show-hogs=<count>    Show most RAM-hungry procs   [default: 0]
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_help: bool,
    flag_warn:  f64,
    flag_crit:  f64,
    flag_show_hogs: usize,
}

fn compare_status(crit: f64, warn: f64, mem: &MemInfo) -> Status {
    match mem.percent_used() {
        Ok(percent) => {
            if percent > crit {
                println!("CRITICAL [check-ram]: {:.1}% > {}%", percent, crit);
                Status::Critical
            } else if percent > warn {
                println!("WARNING [check-ram]: {:.1}% > {}%", percent, warn);
                Status::Warning
            } else {
                println!("OK [check-ram]: {:.1}% < {}%", percent, warn);
                Status::Ok
            }
        },
        Err(e) => {
            println!("UNKNOWN [check-ram]: UNEXPECTED ERROR {:?}", e);
            Status::Unknown
        }
    }
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    let mem = MemInfo::load();
    let status = compare_status(args.flag_crit, args.flag_warn, &mem);
    if args.flag_show_hogs > 0 {
        let per_proc = RunningProcs::currently_running().unwrap();
        let mut procs = per_proc.0.values().collect::<Vec<_>>();
        procs.sort_by(|l, r| r.stat.rss.cmp(&l.stat.rss));
        println!("INFO [check-ram]: ram hogs");
        for process in procs.iter().take(args.flag_show_hogs) {
            let system_kb = mem.total.unwrap();
            let percent = process.percent_ram(system_kb * 1024);
            println!("[{:>6}]{:>5.1}% {:>6}: {}",
                     process.stat.pid,
                     percent,
                     pages_to_human_size(process.stat.rss),
                     process.useful_cmdline());
        }
    };
    status.exit();
}

#[cfg(test)]
mod test {
    use docopt::Docopt;
    use super::{USAGE, Args, compare_status};
    use tabin_plugins::Status;
    use tabin_plugins::procfs::MemInfo;

    #[test]
    fn usage_is_valid() {
        let _: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap();
    }

    #[test]
    fn alerts_when_told_to() {
        let mem = MemInfo {
            total: Some(100),
            available: Some(15),  // 15% free means 85% used
            free: None,
            cached: None
        };
        let crit_threshold = 80.0;

        assert_eq!(compare_status(crit_threshold, 25.0, &mem), Status::Critical);
    }
}