#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2014             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# ails.  You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.

# In some cases the name of the part is not uniq. e.g. for c5900
# devices. In this cases add the color from the dedicated OID to
# the item name.
#
# Example output for this case:
#
#['Toner Cartridge OKI DATA CORP', '100', '30', 'black']
#['Toner Cartridge OKI DATA CORP', '100', '10', 'cyan']
#['Toner Cartridge OKI DATA CORP', '100', '10', 'magenta']
#['Toner Cartridge OKI DATA CORP', '100', '40', 'yellow']
#['Image Drum Unit OKI DATA CORP', '20000', '-409', '']
#['Image Drum Unit OKI DATA CORP', '20000', '7969', '']
#['Image Drum Unit OKI DATA CORP', '20000', '11597', '']
#['Image Drum Unit OKI DATA CORP', '20000', '4621', '']
#['Belt Unit OKI DATA CORP', '60000', '47371', '']
#['Fuser Unit OKI DATA CORP', '60000', '26174', '']
#['Waste Toner box OKI DATA CORP', '1', '-2', '']

# Warn/Crit/upturn current
printer_supply_default_levels = (20.0, 10.0, False)

# When the printer reports -3 as fill threshold the toner
# might be empty or might have some small remaining capacities
# the exact amount is unknown. This makes the nagios state reported
# in this state configurable
printer_supply_some_remaining_status = 1

# Workaround for toners and drum units in c5900 devices
# which have equal names for the single parts.
# Add the color description to that item
# Fix name for toners in DELL devices that have serial
# numbers in item name
def printer_supply_fix_infos(info):
    colors = []
    new_info = []
    for index, line in enumerate(info):
        line_0 = snmp_decode_string(line[0]) # give chance for latin1->utf8 decoding

        # For toners or drum units add the color (if available)
        if line_0.startswith('Toner Cartridge') \
           or line_0.startswith('Image Drum Unit'):
            if line[4]:
                colors += [ line[4] ]
                color = line[4]
            elif line[4] == '':
                color = colors[index - len(colors)]
            line_0 = '%s %s' % (color.title(), line_0)

        if " S/N:" in line_0:
            line_0 = line_0[:line_0.find(" S/N:")]

        new_info.append([line_0] + line[1:])
    return new_info

def inventory_printer_supply(info):
    # Ignore devices which show -2 for current value and -2 for max value -> useless
    # Also fix trailing zero bytes (seen on HP Jetdirect 143)
    return [ (line[0].rstrip('\0'), "printer_supply_default_levels")
             for line in printer_supply_fix_infos(info)
             if not (line[1] == '-2' and line[2] == '-2') and len(line[1]) > 0 ] # ignore useless devices

def check_printer_supply(item, params, info):
    for line in printer_supply_fix_infos(info):
        if line[0].rstrip('\0') == item:
            maxlevel  = int(line[1])
            current   = saveint(line[2])

            # When unit type is
            # 1 = other
            # 3 = supplyThatIsConsumed
            # 4 = supplyThatIsFilled
            # the value is contains the current level if this supply is a container
            # but when the remaining space if this supply is a receptacle
            unit_type = int(line[3])

            if len(params) == 2:
                warn, crit = params # in percent
                upturn = False
            else:
                warn, crit, upturn = params

            # Assume 100% as maximum when 0 is reported
            # Saw some toner cartridge reporting value=0 and maxlevel=0 on empty toner
            if maxlevel == 0:
                maxlevel = 100

            perfdata = [ ("pages", current, warn / 100.0 * maxlevel, crit / 100.0 * maxlevel, 0, maxlevel ) ]

            # handle cases with partial data
            if maxlevel == -2 or current in [ -3, -2, -1 ]: # no percentage possible
                if current == -1 or maxlevel == -1:
                    return (0, "there are no restrictions on this supply")
                elif current == -3:
                    return (printer_supply_some_remaining_status, "Some remaining", perfdata)
                elif current == -2:
                    return (3, "current level is unknown")
                elif maxlevel == -2:
                    # no percentage possible. We compare directly against levels
                    return (0, "current level is %d" % current, [("pages", current)])

            leftperc = 100.0 * float(current) / maxlevel

            if unit_type == 4:
                leftperc = 100 - leftperc
            # Some printers handle the used / remaining material differently
            # With the upturn option we can toggle the point of view (again)
            if upturn:
                leftperc = 100 - leftperc


            infotext = "%.0f%% (levels at %.0f%% / %.0f%%)" % (leftperc, warn, crit)
            if leftperc <= crit:
                return (2, infotext, perfdata)
            elif leftperc <= warn:
                return (1, infotext, perfdata)
            else:
                return (0, infotext, perfdata)

check_config_variables.append("printer_supply_some_remaining_status")

check_info["printer_supply"] = {
    'check_function':          check_printer_supply,
    'inventory_function':      inventory_printer_supply,
    'service_description':     'Supply %s',
    'has_perfdata':            True,
    'snmp_info':               (
        '.1.3.6.1.2.1.43', [
            '11.1.1.6', # Printer-MIB::prtMarkerSuppliesDescription
            '11.1.1.8', # Printer-MIB::prtMarkerSuppliesMaxCapacity
            '11.1.1.9', # Printer-MIB::prtMarkerSuppliesLevel
            '11.1.1.4', # Printer-MIB::prtMarkerSuppliesClass
            '12.1.1.4', # Printer-MIB::prtMarkerColorantValue
        ]
    ),
    'snmp_scan_function':      \
     lambda oid: oid(".1.3.6.1.2.1.43.11.1.1.6.1.1") != None \
                 and "RICOH" not in oid(".1.3.6.1.2.1.1.1.0"),
    'group':                   'printer_supply',
}
