#!/usr/bin/env python
#
#   Copyright Hari Sekhon 2007
#
#   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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# 

#  This plugin for Nagios uses the tw_cli program written by 3ware to
#  get the status of all the arrays or drives on the machine for all 3ware controllers

import os
import re
import sys
from subprocess import Popen, PIPE
from optparse import OptionParser

# Standard Nagios return codes
OK       = 0
WARNING  = 1
CRITICAL = 2
UNKNOWN  = 3

exit = sys.exit
srcdir=os.path.dirname(sys.argv[0])


if os.getuid() != 0:
    print "UNKNOWN: you must be root to run this plugin"
    exit(UNKNOWN)

arch=os.uname()[4]

if re.match("i.86",arch):
    binname="tw_cli"
elif arch == "x86_64":
    binname="tw_cli_64"
else:
    print "UNKNOWN: architecture is not x86 or x86_64, cannot run 3ware utility"
    exit(UNKNOWN)

bin=""
for dir in ( srcdir, "/usr/bin", "/usr/sbin", "/usr/local/bin", "/usr/local/sbin" ):
    if os.path.exists(dir + "/" + binname):
        bin=dir + "/" + binname

if not os.path.exists(bin):
    print "UNKNOWN: 3ware utility for this architecture cannot be found"
    exit(UNKNOWN)

if not os.access(bin,os.X_OK):
    print "UNKNOWN: 3ware utility is not executable"
    exit(UNKNOWN)


def run(cmd):
    #f = os.popen('%s <<< "%s"' % (bin,cmd))
    #return f
    if cmd == "" or cmd == None:
        print "UNKNOWN: internal python error - no cmd supplied for 3ware utility"
        exit(UNKNOWN)
    try:
        p = Popen(bin,stdin=PIPE,stdout=PIPE,stderr=PIPE)
    except OSErrror,e:
        e=str(e)
        if e == "No such file or directory":
            print 'UNKNOWN: Cannot find 3ware utility "%s"' % bin
            exit(UNKNOWN)
        else:
            print "UNKNOWN: code error occurred - %s" % e

    stdout,stderr = p.communicate(cmd)
    
    if stdout == None or stdout == "":
        print "UNKNOWN: No output from 3ware utility"
        exit(UNKNOWN)
    
    output = stdout.split("\n")
   
    if output[1] == "No controller found.":
        print "UNKNOWN: No controllers were found on this machine"
        exit(UNKNOWN)

    stripped_output = output[3:-2]

    if p.returncode != 0:
        stderr = stderr.replace("\n","")
        print "UNKNOWN: 3ware utility returned an exit code of %s. %s" % (p.returncode,stderr)
        exit(UNKNOWN)
    else:
        return stripped_output


def test_raid(verbosity,testdrive=False):

    #f = run("show")
    #lines = f.readlines()[3:-2]
    lines = run("show")
    controllers = [ x.split()[0] for x in lines ]

    status = OK 
    for controller in controllers:
        if testdrive == True:
            #f = run("/%s show drivestatus" % controller)
            #drives = f.readlines()[3:-2]
            drives = run("/%s show drivestatus" % controller)
            if verbosity >= 3:
                for z in drives:
                    print z,
                print

            for x in drives:
                y = x.split()
                if y[1] == "OK" or y[1] == "NOT-PRESENT":
                    continue
                else:
                    drive = int(y[0][1:])
                    array = int(y[2][1:])
                    condition = y[1]
                    print 'RAID CRITICAL: Status of drive in port %s is "%s" (Array %s on adapter %s)' % (drive,condition,array,controller[1:])
                    status = CRITICAL
        else:
            #f = run("/%s show unitstatus" % controller)
            #units = f.readlines()[3:-2]
            units = run("/%s show unitstatus" % controller)
            if verbosity >= 3:
                for z in units:
                    print z,
                print

            for x in units:
                y = x.split()
                state = y[2]
                if state == "OK":
                     continue
                elif state == "REBUILDING":
                    unit = int(y[0][1])
                    raid = y[1]
                    condition = y[2]
                    percent_complete = y[3]
                    print 'RAID WARNING: Array %s status is "%s" (%s on adapter %s) - Rebuild Status: %s%% complete' % (unit,condition,raid,controller[1:],percent_complete)
                    if status == OK:
                        status = WARNING
                else:
                    unit = int(y[0][1])
                    raid = y[1]
                    condition = y[2]
                    print 'RAID CRITICAL: Array %s status is "%s" (%s on adapter %s)' % (unit,condition,raid,controller[1:])
                    status = CRITICAL


    if status == OK:
        if testdrive == True:
            print "RAID OK: All drives OK"
        else:
            print "RAID OK: All arrays OK"
        return OK
    elif status == WARNING:
        return WARNING
    elif status == CRITICAL:
        return CRITICAL
    else:
        return UNKNOWN
    

def main():
    parser = OptionParser()
    parser.add_option("-d","--drivestatus",action="store_true",dest="testdrive",help="Check each drive status instead of Raid status")
    parser.add_option("-v","--verbose",action="count",dest="verbosity",help="Verbose mode. Good for testing plugin. By default only one result line is printed as per Nagios standards")

    (options,args) = parser.parse_args()

    testdrive = options.testdrive
    verbosity = options.verbosity

    result = test_raid(verbosity,testdrive)

    exit(result)

if __name__ == "__main__":
    main()
