#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2010             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.

# Example output of agent:
#<<<smbios_sel>>>
#1 2010-03-29 09:30:36 Single-bit ECC memory error
#1 2010-03-29 09:30:36 Single-bit ECC memory error
#1 2010-03-29 09:30:37 Single-bit ECC memory error
#1 2010-03-29 09:30:38 Single-bit ECC memory error
#1 2010-03-29 09:30:38 Single-bit ECC memory error
#1 2010-03-29 09:30:39 Single-bit ECC memory error
#1 2010-03-29 09:30:39 Single-bit ECC memory error
#1 2010-03-29 09:30:40 Single-bit ECC memory error
#
# Known message types:
#struct message messages[] = {
#  {0x00,  "Unknown message type" },
#  {0x01,  "Single-bit ECC memory error" },
#  {0x02,  "Multi-bit ECC memory error" },
#  {0x03,  "Parity memory error" },
#  {0x04,  "Bus time-out" },
#  {0x05,  "I/O Channel Check" },
#  {0x06,  "Software NMI" },
#  {0x07,  "POST Memory Resize" },
#  {0x08,  "POST Error" },
#  {0x09,  "PCI Parity Error" },
#  {0x0A, "PCI System Error" },
#  {0x0B, "CPU Failure" },
#  {0x0C, "EISA FailSafe Timer time-out" },
#  {0x0D, "Correctable memory log disabled" },
#  {0x0E, "Logging disabled for a specific Event Type - too many errors of the same type received in a short amount of time" },
#  {0x0F, "Unknown message type" },
#  {0x10, "System Limit Exceeded (e.g. voltage or temperature threshold)" },
#  {0x11, "Asynchronous hardware timer expired and issued a system reset" },
#  {0x12, "System configuration information" },
#  {0x13, "Hard-disk information" },
#  {0x14, "System reconfigured" },
#  {0x15, "Uncorrectable CPU-complex error" },
#  {0x16, "Log Area Reset/Cleared" },
#  {0x17, "System boot" }
#   0x18-0x7F  Unused, available for assignment by this specification.
#   0x80-0xFE  Available for system- and OEM-specific assignments.
#   0xFF       End-of-Log
#};

import datetime, time

smbios_sel_msg_types = {
  #Type: (Number-of-Entries Threshold, Status-Code, Current Count, Last Entry, Alias)
   0: [1, 1, 0, None, None],
   1: [5, 2, 0, None, None],
   2: [1, 2, 0, None, None],
   3: [1, 2, 0, None, None],
   4: [1, 2, 0, None, None],
   5: [1, 1, 0, None, None],
   6: [1, 0, 0, None, None],
   7: [1, 0, 0, None, None],
   8: [1, 2, 0, None, None],
   9: [1, 2, 0, None, None],
  10: [1, 2, 0, None, None],
  11: [1, 2, 0, None, None],
  12: [1, 0, 0, None, None],
  13: [1, 2, 0, None, None],
  14: [1, 1, 0, None, None],
  15: [1, 0, 0, None, None],
  16: [1, 2, 0, None, None],
  17: [1, 1, 0, None, None],
  18: [1, 0, 0, None, None],
  19: [1, 0, 0, None, None],
  20: [1, 0, 0, None, None],
  21: [1, 2, 0, None, None],
  22: [1, 0, 0, None, None],
  23: [1, 0, 0, None, None],
}

def inventory_smbios_sel(info):
    if len(info) > 0:
        return [(None, "", "None")]

def check_smbios_sel(_no_item, _no_params, info):
    if len(info) == 0:
        return (0, "OK - The System Event Log is empty")

    sum_status = 0
    num_errors = 0

    for line in info:
        type = line[0]
        isodate = " ".join(line[1:3])
        msg = " ".join(line[3:])

        # Errors reported by the agent result in CRITICAL state
        # Each error will be listed as single log
        if type == "E":
            sum_status = 2
            num_errors += 1;
            smbios_sel_msg_types["E%d"%num_errors] = [1, 2, 1, datetime.datetime.now(), msg]
            continue

        # Informational messages are explicit created by our binary when the
        # sel is empty. This is needed to create the smbios_sel service in Nagios.
        if type == "I":
            sum_status = 0
            smbios_sel_msg_types["I%d"%num_errors] = [1, 0, 1, datetime.datetime.now(), msg]
            continue

        type = int(type)
        if type in smbios_sel_msg_types:
            # Increase counter
            smbios_sel_msg_types[type][2] = smbios_sel_msg_types[type][2]+1;

            # Dynamically set the alias if none set yet
            if smbios_sel_msg_types[type][4] == None:
                smbios_sel_msg_types[type][4] = msg

            # Check if num threshold reached and set new sum_status
            # when this status is worse than the current sum_status
            if smbios_sel_msg_types[type][0] <= smbios_sel_msg_types[type][2]:
                if smbios_sel_msg_types[type][1] > sum_status:
                    sum_status = smbios_sel_msg_types[type][1]

            # Save the date when the current entry is newer
            dt = datetime.datetime(*time.strptime(isodate, "%Y-%m-%d %H:%M:%S")[0:5])
            if smbios_sel_msg_types[type][3] == None or smbios_sel_msg_types[type][3] < dt:
                smbios_sel_msg_types[type][3] = dt;
        else:
            # Unknown types will result in UNKNOWN state
            sum_status = 3
            smbios_sel_msg_types[type] = [1, 3, 1,
                                          datetime.datetime.now(),
                                          "Unknown log type (%s)" % type]

    sum_output = ""
    for type in smbios_sel_msg_types:
        if smbios_sel_msg_types[type][2] > 0:
            sum_output += "%s (Num: %d Last: %s) " % \
                          (smbios_sel_msg_types[type][4],
                           smbios_sel_msg_types[type][2],
                           smbios_sel_msg_types[type][3])

    return (sum_status, "%s: %s" % (nagios_state_names[sum_status], sum_output))

check_info['smbios_sel'] = (check_smbios_sel, "SMBIOS Event Log", 0, inventory_smbios_sel)
