#!/usr/local/bin/python
#Copyright (c) 2012, Jakob Borg
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions are met:
#    * Redistributions of source code must retain the above copyright
#      notice, this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in the
#      documentation and/or other materials provided with the distribution.
#    * The name of the author may not be used to endorse or promote products
#      derived from this software without specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY JAKOB BORG ''AS IS'' AND ANY EXPRESS OR IMPLIED
#WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
#EVENT SHALL JAKOB BORG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
#OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
#IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
#OF SUCH DAMAGE.
# Initial code taken from: https://github.com/jm66/solaris-extra-snmp
BASE_OID = '.1.3.6.1.4.1.25359.1'

import sys
import commands
import re
import snmpresponse

# we do not have kstat in freebsd, so will implement this later
#def kstat(name):
    #output = commands.getoutput("kstat -p " + name)
    #try:
        #return int(re.split("\s+", output)[1])
    #except:
        #return 0

#def zfs_arc_size(oid):
    #return ('gauge', kstat("zfs:0:arcstats:size") / 1024) # KB

#def zfs_arc_data(oid):
    #return ('gauge', kstat("zfs:0:arcstats:data_size") / 1024) # KB

#def zfs_arc_meta(oid):
    #return ('gauge', kstat("zfs:0:arcstats:meta_used") / 1024) # KB

#def zfs_arc_hits(oid):
    #return ('counter', kstat("zfs:0:arcstats:hits") % 2**32) # 32 bit counter

#def zfs_arc_misses(oid):
    #return ('counter', kstat("zfs:0:arcstats:misses") % 2**32) # 32 bit counter

#def zfs_arc_c(oid):
    #return ('gauge', kstat("zfs:0:arcstats:c") / 1024) # KB

#def zfs_arc_p(oid):
    #return ('gauge', kstat("zfs:0:arcstats:p") / 1024) # KB  

#def zfs_read(oid):
    #return ('counter', kstat("unix:0:vopstats_zfs:read_bytes") / 1024 % 2**32) # 32 bit KB counter

#def zfs_readdir(oid):
    #return ('counter', kstat("unix:0:vopstats_zfs:readdir_bytes") / 1024 % 2**32) # 32 bit KB counter

#def zfs_write(oid):
    #return ('counter', kstat("unix:0:vopstats_zfs:write_bytes") / 1024 % 2**32) # 32 bit KB counter

#def zfs_l2arc_hits(oid):
    #return ('counter', kstat("zfs:0:arcstats:l2_hits") % 2**32) # 32 bit counter

#def zfs_l2arc_misses(oid):
    #return ('counter', kstat("zfs:0:arcstats:l2_misses") % 2**32) # 32 bit counter

#def zfs_l2arc_write(oid):
    #return ('counter', kstat("zfs:0:arcstats:l2_write_bytes") / 1024 % 2**32) # 32 bit KB counter

#def zfs_l2arc_read(oid):
    #return ('counter', kstat("zfs:0:arcstats:l2_read_bytes") / 1024 % 2**32) # 32 bit KB counter

def zfs_pools():
    statuses = { "ONLINE": 1, "DEGRADED": 2, "FAULTED": 3 }
    pools = [ re.split('\s+', line) for line in commands.getoutput("zpool list -H -o name,health").split("\n") ]
    pools = [ ( row[0], statuses.get(row[1], 4) ) for row in pools ]
    return pools

def zfs_used_avail(fs):
    return [ int(x) / 1024 for x in commands.getoutput("zfs get -Hpo value used,available " + fs).split("\n")]

def zfs_used(fs, divisor=1):
    return ('gauge', zfs_used_avail(fs)[0] / divisor)

def zfs_avail(fs, divisor=1):
    return ('gauge', zfs_used_avail(fs)[1] / divisor)

def zfs_size(fs, divisor=1):
    return ('gauge', ( zfs_used_avail(fs)[0] + zfs_used_avail(fs)[1] ) / divisor)

def zfs_vols():
    vols = [ re.split('\s+', line) for line in commands.getoutput("zfs list -H -t volume").split("\n")]
    vols = [ ( row[0] ) for row in vols ]
    return vols

# nymnetworks     OBJECT IDENTIFIER ::= {enterprises 25359}
# zfs             OBJECT IDENTIFIER ::= {nymnetworks 1}
# fs              OBJECT IDENTIFIER ::= {zfs 1}
# arc             OBJECT IDENTIFIER ::= {zfs 2}
# l2arc           OBJECT IDENTIFIER ::= {zfs 3}
# io              OBJECT IDENTIFIER ::= {zfs 4}
# vols            OBJECT IDENTIFIER ::= {zfs 5}

# To add later as now we ship with what we have
#result = [
    #( BASE_OID + '.2.1.0', zfs_arc_size),
    #( BASE_OID + '.2.2.0', zfs_arc_meta),
    #( BASE_OID + '.2.3.0', zfs_arc_data),
    #( BASE_OID + '.2.4.0', zfs_arc_hits),
    #( BASE_OID + '.2.5.0', zfs_arc_misses),
    #( BASE_OID + '.2.6.0', zfs_arc_c),
    #( BASE_OID + '.2.7.0', zfs_arc_p),

    #( BASE_OID + '.3.1.0', zfs_l2arc_hits),
    #( BASE_OID + '.3.2.0', zfs_l2arc_misses),
    #( BASE_OID + '.3.3.0', zfs_l2arc_read),
    #( BASE_OID + '.3.4.0', zfs_l2arc_write),

    #( BASE_OID + '.4.1.0', zfs_read),
    #( BASE_OID + '.4.2.0', zfs_readdir),
    #( BASE_OID + '.4.3.0', zfs_write),
#]
result = []
i = 1
for pool, health in zfs_pools():
    result.append((BASE_OID + '.1.1.' + str(i), ('string', pool)))
    result.append((BASE_OID + '.1.2.' + str(i), lambda oid, fs=pool: zfs_avail(fs)))
    result.append((BASE_OID + '.1.3.' + str(i), lambda oid, fs=pool: zfs_used(fs)))
    result.append((BASE_OID + '.1.4.' + str(i), ('integer', health)))
    result.append((BASE_OID + '.1.5.' + str(i), lambda oid, fs=pool: zfs_size(fs)))
    result.append((BASE_OID + '.1.12.' + str(i), lambda oid, fs=pool: zfs_avail(fs, 1024)))
    result.append((BASE_OID + '.1.13.' + str(i), lambda oid, fs=pool: zfs_used(fs, 1024)))
    result.append((BASE_OID + '.1.14.' + str(i), lambda oid, fs=pool: zfs_size(fs, 1024)))
    i += 1

i = 1
if zfs_vols()[0] != '':
    for vol in zfs_vols():
        result.append((BASE_OID + '.5.1.' + str(i), ('string', vol)))
        result.append((BASE_OID + '.5.2.' + str(i), lambda oid, fs=vol: zfs_avail(fs)))
        result.append((BASE_OID + '.5.3.' + str(i), lambda oid, fs=vol: zfs_used(fs)))
        result.append((BASE_OID + '.5.4.' + str(i), lambda oid, fs=vol: zfs_size(fs)))
        result.append((BASE_OID + '.5.12.' + str(i), lambda oid, fs=vol: zfs_avail(fs, 1024)))
        result.append((BASE_OID + '.5.13.' + str(i), lambda oid, fs=vol: zfs_used(fs, 1024)))
        result.append((BASE_OID + '.5.14.' + str(i), lambda oid, fs=vol: zfs_size(fs, 1024)))
        i += 1

operation = sys.argv[1]
req_oid = sys.argv[2]

snmpresponse.respond_to(operation, req_oid, result)

