#!/opt/rocks/bin/python

# Ganglia plugin to monitor disk statistics from iostat

# want to get extended output as in iostat command:
# iostat -k -x 1 2

# potential problem is that the above takes 1s to run

# reference: http://sourceforge.net/apps/trac/ganglia/browser/trunk/monitor-core/gmond/python_modules/network/tcpconn.py

import os
import re
import subprocess
import sys
import time

# globals to hold iostat output (should be in a class, probably)
_iostat_data = {}
_iostat_time = 0.0
_iostat_interval = 2.0
_iostat_lifetime = 30.0

def fill_iostat():
    # Actually call the iostat command and save output

    global _iostat_data
    global _iostat_time
    global _iostat_interval
    global _iostat_lifetime

    interval = str(int(_iostat_interval))
    iostat_cmd = [ '/usr/bin/iostat', '-k', '-t', '-x', interval, '2' ]
    iostat_output = subprocess.Popen(iostat_cmd, stdout=subprocess.PIPE)
    
    # skip the first batch of output
    flag = 'Time:'
    saw_flag = False
    for aline in iostat_output.stdout:
        aline = aline.rstrip('\r\n')
        words = aline.split()
        if words == []:
            continue
        if words[0] == flag:
            if saw_flag:
                break
            else:
                saw_flag = True

    # now read second batch of data, storing output
    # assumming data lines start with "sd"
    _iostat_data = {}
    for aline in iostat_output.stdout:
        aline = aline.rstrip('\r\n')
        words = aline.split()
        if words == []:
            continue
        if re.search('^sd', words[0]):
            mydev = words[0]
            mydata = {
                'rrqmhz'   : words[1],
                'wrqmhz'   : words[2],
                'rhz'      : words[3],
                'whz'      : words[4],
                'rkbhz'    : words[5],
                'wkbhz'    : words[6],
                'avgrq-sz' : words[7],
                'avgqu-sz' : words[8],
                'await'    : words[9],
                'svctm'    : words[10],
                'util'     : words[11]
                }
            _iostat_data[mydev] = mydata
    
    _iostat_time = time.time()
    #print _iostat_data
    #print 'sda is', _iostat_data['sda']
    return

def get_iostat(device, stat):
    # return data item from _iostat_data structure
    # update _iostat_data if outdated
    
    global _iostat_data
    global _iostat_time
    global _iostat_interval
    global _iostat_lifetime

    now = time.time()
    #print "delta time", now - _iostat_time, "lifetime is", _iostat_lifetime
    if (now - _iostat_time) > _iostat_lifetime:
        fill_iostat()

    result = _iostat_data[device][stat]

    return result

def iostat_handler(name):

    adev, astat = name.split('_')

    result = 0.0
    result = float(get_iostat(adev, astat))
    print "for device", adev, "stat", astat, "have", result
    
    return result

# global list that defines generalities of metrics.
# these get modfied with specific mount or device names
# before being returned to gmond during initialization.
_generic_desc = [
    {'name' : 'rrqmhz',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': 'Hz',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk read requests merged queued per second',
     'groups': 'iostat'
     },
    {'name' : 'wrqmhz',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': 'Hz',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk write requests merged queued per second',
     'groups': 'iostat'
     },
    {'name' : 'rhz',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': 'Hz',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk read requests to device per second',
     'groups': 'iostat'
     },
    {'name' : 'whz',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': 'Hz',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk write requests to device per second',
     'groups': 'iostat'
     },
    {'name' : 'rkbhz',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': 'kB Hz',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk read kiloBytes per second',
     'groups': 'iostat'
     },
    {'name' : 'wkbhz',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': 'kB Hz',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk write kiloBytes per second',
     'groups': 'iostat'
     },
    {'name' : 'avgrq-sz',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': '512 byte sectors',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk average device request size in sectors',
     'groups': 'iostat'
     },
    {'name' : 'avgqu-sz',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': 'N Queued',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk average queue length of requests issued to device',
     'groups': 'iostat'
     },
    {'name' : 'await',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': 'ms',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk average total request service time',
     'groups': 'iostat'
     },
    {'name' : 'svctm',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': 'ms',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk average device request service time',
     'groups': 'iostat'
     },
    {'name' : 'util',
     'call_back': iostat_handler,
     'time_max': 90,
     'value_type': 'float',
     'units': 'Percent',
     'slope': 'both',
     'format': '%f',
     'description': 'Disk percent utilization',
     'groups': 'iostat'
     }
    ]
    
def metric_init(params):

    global descriptors
    descriptors = []
    global _iostat_interval
    global _iostat_lifetime

    #print params

    # if these params defined in config, use them
    if params.has_key('iostatinterval'):
        _iostat_interval = float(params['iostatinterval'])

    if params.has_key('iostatlifetime'):
        _iostat_lifetime = float(params['iostatlifetime'])

    #print "lifetime is", _iostat_lifetime
    #print "interval is ", _iostat_interval

    for aparam, avalue in params.iteritems():
        #print avalue
        if avalue == 'disk':
            adev, astat = aparam.split('_')

            #print "device is", adev, "stat is", astat
            adict = {}
            for adict in _generic_desc:
                if adict['name'] == astat:
                    adesc = adict
                    adesc['name'] = aparam
                    descriptors.append(adesc)

    return descriptors
    

def metric_cleanup():
    '''Clean up the metric module.'''
    pass 

#This code is for debugging and unit testing
if __name__ == '__main__':
    metric_init(None)
    for d in descriptors:
        v = d['call_back'](d['name'])
        print 'value for %s is %s' % (d['name'],  v)