#!/usr/bin/env python3
# -*- coding: utf-8; py-indent-offset: 4 -*-
#
# Author:  Linuxfabrik GmbH, Zurich, Switzerland
# Contact: info (at) linuxfabrik (dot) ch
#          https://www.linuxfabrik.ch/
# License: The Unlicense, see LICENSE file.

# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md

"""See the check's README for more details."""

import argparse
import re
import sys

import lib.args
import lib.base
import lib.lftest
import lib.shell
from lib.globals import STATE_OK, STATE_UNKNOWN, STATE_WARN

__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
__version__ = '2026040801'

DESCRIPTION = """Checks the IPMI System Event Log (SEL) for entries and alerts when events are found.
Entries can be filtered by regex using --ignore. To clear the SEL after resolving
issues, run "ipmitool sel clear".
Requires root or sudo."""


def parse_args():
    """Parse command line arguments using argparse."""
    parser = argparse.ArgumentParser(description=DESCRIPTION)

    parser.add_argument(
        '-V',
        '--version',
        action='version',
        version=f'%(prog)s: v{__version__} by {__author__}',
    )

    parser.add_argument(
        '--authtype',
        help='Authentication type for IPMIv1.5 lan session activation. '
        'Supported types are NONE, PASSWORD, MD2, MD5, or OEM. '
        'Default: %(default)s',
        dest='V15AUTHTYPE',
        choices=['NONE', 'PASSWORD', 'MD2', 'MD5', 'OEM'],
        default='NONE',
    )

    parser.add_argument(
        '-H',
        '--hostname',
        help='Remote server address, can be a hostname or IP address. '
        'Required for lan and lanplus interfaces.',
        dest='HOSTNAME',
        default=None,
    )

    parser.add_argument(
        '--ignore',
        help='Ignore SEL entries matching this Python regular expression. '
        'Can be specified multiple times. '
        'Example: `--ignore="Log area reset/cleared"`.',
        dest='IGNORE',
        action='append',
        default=None,
    )

    parser.add_argument(
        '--interface',
        help='IPMI interface to use. '
        'Supported types are "lan" (IPMI v1.5) or "lanplus" (IPMI v2.0). '
        'Default: %(default)s',
        dest='INTERFACE',
        choices=['lan', 'lanplus'],
        default='lan',
    )

    parser.add_argument(
        '--password',
        help='Remote server password.',
        dest='PASSWORD',
    )

    parser.add_argument(
        '--port',
        help='Remote server UDP port to connect to. Default: %(default)s',
        dest='PORT',
        default=623,
    )

    parser.add_argument(
        '--privlevel',
        help='Force session privilege level. '
        'Can be CALLBACK, USER, OPERATOR, ADMINISTRATOR. '
        'Default: %(default)s',
        dest='PRIVLEVEL',
        choices=['CALLBACK', 'USER', 'OPERATOR', 'ADMINISTRATOR'],
        default='USER',
    )

    parser.add_argument(
        '--test',
        help=lib.args.help('--test'),
        dest='TEST',
        type=lib.args.csv,
    )

    parser.add_argument(
        '--username',
        help='Remote server username. Default: %(default)s',
        dest='USERNAME',
        default='NULL',
    )

    args, _ = parser.parse_known_args()
    return args


def main():
    """The main function. This is where the magic happens."""

    # parse the command line
    try:
        args = parse_args()
    except SystemExit:
        sys.exit(STATE_UNKNOWN)

    # init some vars
    if args.IGNORE is None:
        args.IGNORE = []

    # compile ignore patterns
    try:
        ignore_patterns = [re.compile(p) for p in args.IGNORE]
    except re.error as e:
        lib.base.cu(f'Invalid regular expression: {e}')

    # fetch data
    if args.TEST is None:
        cmd = 'ipmitool sel elist '
        if args.HOSTNAME:
            # use ipmi with remote parameters
            cmd += f'-A {args.V15AUTHTYPE} ' if args.INTERFACE == 'lan' else ''
            cmd += f'-H {args.HOSTNAME} ' if args.HOSTNAME else ''
            cmd += f'-I {args.INTERFACE} ' if args.INTERFACE else ''
            cmd += f'-L {args.PRIVLEVEL} ' if args.PRIVLEVEL else ''
            cmd += f'-p {args.PORT} ' if args.PORT else ''
            cmd += f'-P {args.PASSWORD} ' if args.PASSWORD else ''
            cmd += f'-U {args.USERNAME} ' if args.USERNAME else ''
        # execute the shell command and return its result and exit code
        stdout, stderr, retc = lib.base.coe(lib.shell.shell_exec(cmd))
    else:
        # do not call the command, put in test data
        stdout, stderr, retc = lib.lftest.test(args.TEST)

    if retc != 0:
        lib.base.cu(stderr)

    # everything is ok
    if 'SEL has no entries' in stderr:
        lib.base.oao('Everything is ok.', STATE_OK)

    # build the message
    msg = ''
    for line in reversed(stdout.splitlines()):
        if not line.strip():
            continue
        if any(pattern.search(line) for pattern in ignore_patterns):
            continue
        msg += f'* {line.replace("|", ";")}\n'

    if not msg:
        lib.base.oao('Everything is ok.', STATE_OK)

    # over and out
    lib.base.oao(msg, STATE_WARN)


if __name__ == '__main__':
    try:
        main()
    except Exception:
        lib.base.cu()
