#!/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-
# tails. 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
factory_settings["printer_supply_default_levels"] = {
    "levels" : (20.0, 10.0),
}

# 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

# TODO: This variable is deprecated. Remove it in future
check_config_variables.append("printer_supply_some_remaining_status")

# 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, (description, max_capacity, level, class_, color) in enumerate(info):
        # give chance for latin1->utf8 decoding
        line_0 = description

        # For toners or drum units add the color (if available)
        if line_0.startswith('Toner Cartridge') \
           or line_0.startswith('Image Drum Unit'):
            if color:
                colors += [ color ]
            elif color == '' and colors:
                color = colors[index - len(colors)]
            else:
                color = None
            if color:
                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, max_capacity, level, class_, color])
    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 [(description.rstrip('\0'), {})
            for description, max_capacity, level, class_, color in printer_supply_fix_infos(info)
            # ignore useless devices
            if not (max_capacity == '-2' and level == '-2') and level and len(max_capacity) > 0 ]


def check_printer_supply(item, params, info):
    if type(params) == tuple:
        if len(params) == 2:
            params = { "levels" : params }
        else:
            params = {
                "levels" : params[:2],
                "upturn_toner" : params[2],
            }

    for description, max_capacity, level, class_, color in printer_supply_fix_infos(info):
        if description.rstrip('\0') == item:
            max_capacity = int(max_capacity)
            level = saveint(level)

            color_info = ""
            if color and color.lower() not in item.lower():
                color_info = "[%s] " % color

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

            warn, crit = params["levels"]
            perfdata = [("pages", level, warn / 100.0 * max_capacity,
                        crit / 100.0 * max_capacity, 0, max_capacity)]

            # handle cases with partial data
            if max_capacity == -2 or level in [ -3, -2, -1 ]: # no percentage possible
                if level == -1 or max_capacity == -1:
                    return 0, "%sThere are no restrictions on this supply" % color_info
                elif level == -3:
                    return params.get("some_remaining", printer_supply_some_remaining_status),\
                            "%sSome remaining" % color_info, perfdata
                elif level == -2:
                    return 3, "%s Unknown level" % color_info
                elif max_capacity == -2:
                    # no percentage possible. We compare directly against levels
                    return 0, "%sLevel: %d" % (color_info, level), [("pages", level)]

            leftperc = 100.0 * float(level) / max_capacity
            # 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
            #
            # This table can be missing on some devices. Assume type 3 in this case.
            if class_ == '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 params.get("upturn_toner", False):
                leftperc = 100 - leftperc

            infotext = "%sRemaining: %.0f%%" % (color_info, leftperc)
            state = 0
            if leftperc <= crit:
                state = 2
            elif leftperc <= warn:
                state = 1
            if state:
                infotext += " (warn/crit at %.0f%%/%.0f%%)" % (warn, crit)
            return state, infotext, perfdata


check_info["printer_supply"] = {
    'inventory_function'        : inventory_printer_supply,
    'check_function'            : check_printer_supply,
    'service_description'       : 'Supply %s',
    '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 \
                                          # exclude ricoh printer
                                          and oid(".1.3.6.1.2.1.1.2.0") != ".1.3.6.1.4.1.367.1.1",
    'has_perfdata'              : True,
    'group'                     : 'printer_supply',
    'default_levels_variable'   : 'printer_supply_default_levels',
}
