#!/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 tryfloat( s):
    try:
        f = float( s)
    except( ValueError):
        error( "Error: Failed to convert '%s' to float" % s, 12)

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

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, total, alertval):
    "True if used > alertval"

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

### Parse the command line
def parseargs( argv, usagefn):
    "Expecting something like: -w <int>[%] -c <int>[%] -p <string> [-m]"
    
    # vals are [ <int>, <percent bool> ]
    warnval = None
    critval = None
    disk    = None
    usemount = 0
    
    i = 1;
    while i < len(argv):
        arg = argv[i]
        if arg == "--help" or arg == "-h":
            usage( argv[0])
            sys.exit(4)
        elif arg == "-w":
            i = i + 1
            if argv[i][-1:] == '%':
                warnval = tryintarr( [ argv[i][:-1], 1])
            else:
                warnval = tryintarr( [ argv[i], 0])
        elif arg == "-c":
            i = i + 1
            if argv[i][-1:] == '%':
                critval = tryintarr( [ argv[i][:-1], 1])
            else:
                critval = tryintarr( [ argv[i], 0])
        elif arg == "-p":
            i = i + 1
            disk = argv[i]
        elif arg == "-m":
            usemount = 1

        i = i + 1

    # basic sanity check
    if not warnval or not critval or not disk 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, disk, usemount


### 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) 2002,2004 Paul Komarek
Copyright (c) 2003,2004 Jacob Joseph <jmjoseph@andrew.cmu.edu>

This plugin uses GNU df to check the amount of free diskspace

Usage: %s -w <int>[%%] -c <int>[%%] -p <disk> [-m]
       %s --help|-h

Where the warning and critical values are minimum amounts free.  If
%% is omitted for the warning or critical values, KB are assumed.

-p <disk> : The partition or mountpoint to check
-m        : causes the output to include the mountpoint(from df) rather
than partition
"""
    print msg % (progname, progname, progname)
    return


def getdf( disk):
    "Get df info for one partition or mountpoint."

    # Assuming that /bin/df is GNU df.
    cmd = "/bin/df"
    if not os.path.isfile( cmd):
        m = "'%s' not found" % cmd
        error( m, 13)

    # Get info.
    fullcmd = "%s %s" % (cmd, disk)
    lines = os.popen( fullcmd, 'r').readlines()

    # Strip first line, concatenate later lines (if lines are too long,
    # df adds carriage returns).
    # FIXME: may break if df breaks the line wher there should be no space
    line = string.join( map(string.strip, lines[1:]))

    # Done.
    return line


def parsedf( disk):
    """Fetch, and parse df line.
    Return (partition, mountpoint, KB free, and percent free) for
    given mountpoint or partition"""

    line = getdf( disk)
    
    # Assuming line is from GNU df.
    tokens = string.split( string.strip( line) )

    # Find final % sign.
    percpos = None
    for i in range( len( tokens)):
        if tokens[i][-1:] == '%':
            percpos = i
            break        
    if percpos is None:
        m = "Error: parsedfline: did not find '%%' in line:\n  %s\n"
        error( m % (line), 14)
    
    # Get partition and mountpoint.
    partition = tokens[0]
    mountpoint = string.join( tokens[percpos+1:])

    # Get kB free.
    kbfree = tokens[percpos-1]
    kbfree = tryint( kbfree)

    # Get percent free
    percfree = tokens[percpos][:-1]
    percfree = tryint( percfree)
    percfree = 100 - percfree

    # Done.
    return partition, mountpoint, kbfree, percfree

def comparefree( percfree, kbfree, limit):
    if limit[1]:
        if percfree <= limit[0]:
            return 1
    else:
        if kbfree <= limit[0]:
            return 1
    return 0


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

    # Get args.
    warnval, critval, disk, usemount = parseargs( sys.argv[:], usage)

    # Get disk info for given location (partition or mountpoint).
    partition, mountpoint, kbfree, percfree  = parsedf( disk)

    # Check value against warnval and critval.
    msg = ""
    if comparefree( percfree, kbfree, critval):
        msg = msg + "DISK CRITICAL - "
        ret = 2
    elif comparefree( percfree, kbfree, warnval):
        msg = msg + "DISK WARNING - "
        ret = 1
    else:
        msg = msg + "DISK OK - "
        ret = 0

    msg = msg + "[%d KB (%d%%) free on " % (kbfree, percfree)
    if usemount:
        msg = msg + "%s]" % mountpoint
    else:
        msg = msg + "%s]" % partition

    print msg
    sys.exit(ret)

    
