#!/usr/bin/env python

# Copyright 2004 Jacob Joseph<jacob@jjoseph.org>
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

import string, sys, os

############################################################################
# Some generic functions( would normally be imported, but I want only
# one file)
############################################################################
def error( msg, err=10):
    sys.stderr.write( msg)
    sys.exit( err)

def tryint( s):
    try:
        i = int( s)
    except( ValueError):
        error( "Error: Failed to convert '%s' to int" % s, 11)
    return i

def tryintarr( arr):
    try:
        iarr = map( int, arr)
    except ValueError:
        error( "Error: failed to convert '%s' to ints", 13)
    return iarr

def tryfloat( s):
    try:
        f = float( s)
    except( ValueError):
        error( "Error: Failed to convert '%s' to float" % s, 12)

def tryfloatarr( arr):
    try:
        farr = map( float, arr)
    except ValueError:
        error( "Error: failed to convert '%s' to floats", 13)
    return farr

def outofrange( val, limitarr):
    if len( limitarr) != 2:
        error( "outofrange() requires limit of len = 2", 14)
    if val <= limitarr[0] or val >= limitarr[1]:
        return 1
    return 0

def comparearr( arr1, limit):
    "true if any element i in arr1 exceeds limit"
    
    if len( arr1) != len( limit):
        error( "compare() requires arrays of equal length.", 15)
    for i in range( len(arr1)):
        if arr1[i] >= limit[i]:
            return 1
    return 0

def compareused( used, max, alertval):
    "True if used > alertval"

    if alertval[1]:
        return( used > max * alertval[0] / 100)
    else:
        return( used > alertval[0])
    
############################################################################

### Parse the command line
def parseargs( argv, usagefn):
    "Expecting something like -w <int>[%] -c <int>[%]"

    # vals are [ <int>, <percent bool> ]
    warnval = None
    critval = None

    i = 1;
    while i < len(argv):
        arg = argv[i]
        if arg == "--help" or arg == "-h":
            usage( argv[0])
            sys.exit(4)
        elif arg == "-w":
            # Convert next arg into int
            i = i + 1
            if argv[i][-1:] == '%':
                warnval = tryintarr( [argv[i][:-1], 1])
            else:
                warnval = tryintarr( [argv[i], 0])
        elif arg == "-c":
            # Convert next arg into int
            i = i + 1
            if argv[i][-1:] == '%':
                critval = tryintarr( [argv[i][:-1], 1])
            else:
                critval = tryintarr( [argv[i], 0])
        i = i + 1

    if not warnval or not critval or \
        len(warnval) != 2 or len(critval) != 2:
        sys.stderr.write( "Failed to parse args.\n")
        usagefn( argv[0])
        sys.exit(5);

    return warnval, critval

### Usage information
def usage( progname):
    msg = """%s
The nagios plugins come with ABSOLUTELY NO WARRANTY. You may redistribute
copies of the plugins under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING.
Copyright (c) 2000 Karl DeBisschop <kdebiss@alum.mit.edu>
Copyright (c) 2002 Paul Komarek
Copyright (c) 2003,2004 Jacob Joseph <jmjoseph@andrew.cmu.edu>

This plugin uses the free command to calculate the total amount of ram
used by real processes.  It subtracts buffers/cache from used.

Usage: %s -w <wsize>[%%] -c <csize>[%%]
       %s --help|-h

Where the warning and critical values are amounts used.  If %% is
omitted, KB is assumed.
"""

    print msg % (progname, progname, progname)
    return


def parsefree():
    "runs free, grabs used from 3rd line.  Returns '[ used, total ]'"
    cmd = "/usr/bin/free -k"
    lines = os.popen( cmd).readlines()

    lnum=0
    for line in lines:
        # grab total
        if lnum == 1:
            total = tryint( string.split(line)[1])
            
        # grab used ram
        if lnum == 2:
            used = tryint( string.split(line)[2])

        # grab used swap
        if lnum == 3:
            swap = tryint( string.split(line)[2])
            break
        lnum = lnum + 1

    used = used + swap
    return [ used, total]

####################################
# "main"
####################################
if __name__ == "__main__":

    # Get args.
    warnval, critval = parseargs( sys.argv, usage)

    # Get VSZ in KB
    used, total = parsefree()

    # Check for warning or critical levels.
    msg = ""
    if compareused( used, total, critval): 
        msg = msg + "VSZ CRITICAL - "
        ret = 2
    elif compareused( used, total, warnval):
        msg = msg + "VSZ WARNING - "
        ret = 1
    else:
        msg = msg + "VSZ OK - "
        ret = 0

    msg = msg + "[%d KB (%d%%) used]" % (used, used*100/total)

    print msg
    sys.exit(ret)
