#!/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 os
import sys

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

try:
    import psutil

    HAVE_PSUTIL = True
except ImportError:
    HAVE_PSUTIL = False


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

DESCRIPTION = """Checks system requirements and kernel settings for MySQL/MariaDB, including swap
configuration, open file limits, and other OS-level parameters that affect database
performance and stability.
Alerts on misconfigured system settings."""

DEFAULT_MAXPORTSALLOWED = 0


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(
        '--always-ok',
        help=lib.args.help('--always-ok'),
        dest='ALWAYS_OK',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '--maxportsallowed',
        help='Maximum number of open ports allowed on this host. 0 disables the check. Default: %(default)s',
        dest='MAXPORTSALLOWED',
        type=int,
        default=DEFAULT_MAXPORTSALLOWED,
    )

    args, _ = parser.parse_known_args()
    return args


def get_opened_ports():
    opened_ports = {}
    if HAVE_PSUTIL:
        opened_ports = psutil.net_connections()
    return len(opened_ports)


def get_sysctl(key):
    cmd = f'sysctl --values {key} 2> /dev/null'
    stdout, _stderr, _retc = lib.base.coe(lib.shell.shell_exec(cmd))
    return stdout.strip()


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

    # logic taken from mysqltuner.pl:get_kernel_info(), v1.9.8

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

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

    # count ports
    opened_ports = get_opened_ports()
    if args.MAXPORTSALLOWED > 0:
        if opened_ports == 0:

            # build the message
            msg += 'No ports checked (psutil missing). '
        if opened_ports > args.MAXPORTSALLOWED:
            state = STATE_WARN
            msg += (
                f'There are too many listening ports:'
                f' {opened_ports} opened >'
                f' {args.MAXPORTSALLOWED} allowed'
                f' (consider dedicating a server for your'
                f' database installation with less services'
                f' running on). '
            )
    perfdata += lib.base.get_perfdata(
        'mysql_opened_ports',
        opened_ports,
        _min=0,
    )

    # get kernel settings on linux
    if lib.base.LINUX:
        result = get_sysctl('vm.swappiness')
        if not result:
            lib.base.cu('Permission denied reading `vm.swappiness`')
        if int(result) > 10:
            state = STATE_WARN
            msg += (
                f'vm.swappiness is {result},'
                f' should be <= 10'
                f' (use `echo 10 >'
                f' /proc/sys/vm/swappiness`). '
            )
        perfdata += lib.base.get_perfdata(
            'mysql_kernel_vm.swappiness',
            result,
            uom='%',
            _min=0,
            _max=100,
        )

        # only if /proc/sys/sunrpc exists
        if os.path.isdir('/proc/sys/sunrpc'):
            result = get_sysctl('sunrpc.tcp_slot_table_entries')
            if int(result) <= 100:
                state = STATE_WARN
                msg += (
                    f'sunrpc.tcp_slot_table_entries is'
                    f' {result}, should be > 100'
                    f' (use `echo 128 > /proc/sys/sunrpc'
                    f'/tcp_slot_table_entries`). '
                )
            perfdata += lib.base.get_perfdata(
                'mysql_kernel_sunrpc.tcp_slot_table_entries',
                result,
                _min=0,
            )

        if os.path.isfile('/proc/sys/fs/aio-max-nr'):
            result = get_sysctl('fs.aio-max-nr')
            if int(result) < 1000000:
                state = STATE_WARN
                msg += (
                    f'fs.aio-max-nr is {result},'
                    f' should be > 1M'
                    f' (use `echo 1048576 >'
                    f' /proc/sys/fs/aio-max-nr`). '
                )
            perfdata += lib.base.get_perfdata(
                'mysql_kernel_fs.aio-max-nr',
                result,
                _min=0,
            )

    if not msg:
        msg = 'Everything is ok.'

    # over and out
    lib.base.oao(msg, state, perfdata, always_ok=args.ALWAYS_OK)


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