#!/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  # pylint: disable=C0413
import sys  # pylint: disable=C0413

import lib.args  # pylint: disable=C0413
import lib.base  # pylint: disable=C0413
import lib.cache  # pylint: disable=C0413
import lib.net  # pylint: disable=C0413
import lib.lftest  # pylint: disable=C0413
import lib.time  # pylint: disable=C0413
import lib.txt  # pylint: disable=C0413
from lib.globals import (STATE_CRIT, STATE_OK,  # pylint: disable=C0413
                          STATE_UNKNOWN, STATE_WARN)

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

DESCRIPTION = """Counts the number of current active DAHDI, SIP or other channels of the Starface
                PBX, and warns on possibly overusage (in percentage).
                It uses the data output of the Starface Monitoring Module, which was
                originally written for Check_MK and listens on port 6556. Supports both IPv4 and
                IPv6. Fetched data is cached up to one minute, so that other Starface plugins
                running in parallel do not query the data again and overload the PBX."""

DEFAULT_CACHE_EXPIRE = 1        # minutes
DEFAULT_CRIT = 90               # %
DEFAULT_HOSTNAME = 'localhost'
DEFAULT_IPV6 = False
DEFAULT_PORT = 6556
DEFAULT_TIMEOUT = 8             # seconds
DEFAULT_WARN = 80               # %


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

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

    parser.add_argument(
        '--always-ok',
        help='Always returns OK.',
        dest='ALWAYS_OK',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '--cache-expire',
        help='The amount of time after which the cached data expires, in minutes. Default: %(default)s',
        dest='CACHE_EXPIRE',
        type=int,
        default=DEFAULT_CACHE_EXPIRE,
    )

    parser.add_argument(
        '--critical',
        help='Set the critical threshold (percentage). Default: %(default)s',
        dest='CRIT',
        default=DEFAULT_CRIT,
    )

    parser.add_argument(
        '-H', '--hostname',
        help='Starface PBX address, can be IP address or hostname. Default: %(default)s',
        dest='HOSTNAME',
        default=DEFAULT_HOSTNAME,
    )

    parser.add_argument(
        '--port',
        help='Starface PBX monitoring port. Default: %(default)s',
        dest='PORT',
        type=int,
        default=DEFAULT_PORT,
    )

    parser.add_argument(
        '--test',
        help='For unit tests. Needs "path-to-stdout-file,path-to-stderr-file,expected-retc".',
        dest='TEST',
        type=lib.args.csv,
    )

    parser.add_argument(
        '--timeout',
        help='Network timeout in seconds. Default: %(default)s (seconds)',
        dest='TIMEOUT',
        type=int,
        default=DEFAULT_TIMEOUT,
    )

    parser.add_argument(
        '--warning',
        help='Set the warning threshold (percentage). Default: %(default)s',
        dest='WARN',
        default=DEFAULT_WARN,
    )

    parser.add_argument(
        '--ipv6',
        help='Use IPv6.',
        dest='USE_IPV6',
        action='store_true',
        default=DEFAULT_IPV6,
    )

    return parser.parse_args()


def main():
    """The main function. Hier spielt die Musik.
    """

    # parse the command line, exit with UNKNOWN if it fails
    try:
        args = parse_args()
    except SystemExit:
        sys.exit(STATE_UNKNOWN)

    # fetch data
    if args.TEST is None:
        # fetch data from PBX, but first from cache
        data = lib.cache.get(args.HOSTNAME, filename='linuxfabrik-monitoring-plugins-starface.db')
        if not data:
            data = lib.base.coe(lib.net.fetch(args.HOSTNAME, args.PORT, timeout=args.TIMEOUT))
            lib.cache.set(args.HOSTNAME, data, lib.time.now() + args.CACHE_EXPIRE*60, filename='linuxfabrik-monitoring-plugins-starface.db')
    else:
        # do not call the command, put in test data
        stdout, stderr, retc = lib.lftest.test(args.TEST)
        data = stdout

    if not data:
        lib.base.cu('Got no valuable response from {}:{}.'.format(args.HOSTNAME, args.PORT))

    # extract data
    result = lib.txt.extract_str(data, '<<<starface_channels>>>', '<<<').splitlines()

    # init some vars
    msg= ''
    state = STATE_OK
    perfdata = ''

    max_calls = 0
    cnt_dahdi, cnt_sip, cnt_other = 0, 0, 0

    # build the message
    for row in result:
        if not row:
            continue

        if row.endswith('max calls'):
            max_calls = row.replace(' max calls', '')
        else:
            if row.startswith('DAHDI/'):
                cnt_dahdi += 1
            elif row.startswith('SIP/'):
                cnt_sip += 1
            else:
                cnt_other += 1

    if int(max_calls) != 0:
        # there is a maximum call limit, warn on overusage
        used_percent = round(float(cnt_dahdi + cnt_sip + cnt_other) / float(max_calls) * 100, 1)
        state = lib.base.get_state(used_percent, args.WARN, args.CRIT)
        msg += '{}% call usage ({}/{}){}. '.format(
            used_percent,
            cnt_dahdi + cnt_sip + cnt_other,
            max_calls,
            lib.base.state2str(state, prefix=' '),
        )
        perfdata += lib.base.get_perfdata('used_percent', used_percent, '%', args.WARN, args.CRIT, 0, 100)
        perfdata += lib.base.get_perfdata('max_calls', max_calls, None, None, None, 0, None)

    if cnt_dahdi or cnt_sip or cnt_other:
        msg += 'Current channels: '
        msg += '{}x DAHDI, '.format(cnt_dahdi) if cnt_dahdi else ''
        msg += '{}x SIP, '.format(cnt_sip) if cnt_sip else ''
        msg += '{}x other, '.format(cnt_other) if cnt_other else ''
    else:
        msg += 'No channels active.  '
    perfdata += lib.base.get_perfdata('channel_dahdi', cnt_dahdi, None, None, None, 0, max_calls if max_calls else None)
    perfdata += lib.base.get_perfdata('channel_sip', cnt_sip, None, None, None, 0, max_calls if max_calls else None)
    perfdata += lib.base.get_perfdata('channel_other', cnt_other, None, None, None, 0, max_calls if max_calls else None)

    # over and out
    lib.base.oao(msg[:-2], state, perfdata, always_ok=args.ALWAYS_OK)


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