#!/usr/lib64/linuxfabrik-monitoring-plugins/venv/bin/python
# -*- 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 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__ = '2026102403'

DESCRIPTION = """Verifies that the current tuned profile matches the expected setting.
Alerts when the profile in force is not the expected one, when no profile is active,
or when the tuned daemon is not running and the profile is therefore not applied.
Useful for ensuring consistent performance tuning across a fleet of servers."""

CMD = ['tuned-adm', 'active']
DEFAULT_TIMEOUT = 8
DEFAULT_TUNED_PROFILE = 'virtual-guest'
KERNEL_SETTINGS_PROFILE = 'kernel_settings'


def parse_args():
    """Parse command line arguments using argparse."""
    parser = argparse.ArgumentParser(
        description=DESCRIPTION,
        epilog=lib.args.epilog(__file__),
        formatter_class=lib.args.HelpFormatter,
    )

    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(
        '--profile',
        default=DEFAULT_TUNED_PROFILE,
        dest='TUNED_PROFILE',
        help='Expected tuned profile name. '
        'Case-insensitive. '
        'Example: `--profile=virtual-guest`. '
        'Default: %(default)s',
    )

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

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

    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)

    # fetch data
    if args.TEST is None:
        success, result = lib.shell.shell_exec(CMD, timeout=args.TIMEOUT)
        if not success and result.startswith('Timeout after'):
            lib.base.oao(
                f'Timeout after {args.TIMEOUT}s while running `{" ".join(CMD)}`.',
                STATE_WARN,
                always_ok=args.ALWAYS_OK,
            )
        if not success:
            lib.base.cu('tuned is not applicable to your system.')
        stdout, _stderr, _retc = result
    else:
        # do not call the command, put in test data
        stdout, _stderr, _retc = lib.lftest.test(args.TEST)

    # init some vars
    active = post_loaded = preset = None

    # analyze data
    # `tuned-adm active` prints one of these (tuned-adm/admin.py, verified with
    # tuned 2.27 on Rocky 9):
    #   Current active profile: <profile> [<post-loaded profile>]
    #   Current post-loaded profile: <post-loaded profile>     (optional second line)
    #   No current active profile.                              (exit code 1)
    #   It seems that tuned daemon is not running, ...          (exit code 0)
    #   Preset profile: <profile>
    for line in stdout.splitlines():
        key, _, value = line.partition(':')
        if key == 'Current active profile':
            active = value.strip()
        elif key == 'Current post-loaded profile':
            post_loaded = value.strip()
        elif key == 'Preset profile':
            preset = value.strip()
    expected = args.TUNED_PROFILE.lower()

    # build the message
    if 'tuned daemon is not running' in stdout:
        msg = (
            f'tuned is not running, so the profile "{preset}" is not applied. Start'
            ' it with `sudo systemctl enable --now tuned`.'
        )
        state = STATE_WARN
    elif active is None:
        msg = (
            f'No tuned profile is active, but supposed to be "{args.TUNED_PROFILE}".'
            f' Activate it with `sudo tuned-adm profile {args.TUNED_PROFILE}`.'
        )
        state = STATE_WARN
    else:
        # The active profile includes the post-loaded one, and both forms are what an
        # admin may have configured as expected.
        candidates = [active.lower()]
        if post_loaded and active.endswith(f' {post_loaded}'):
            candidates.append(active[: -len(post_loaded) - 1].lower())
        # The kernel_settings role of linux-system-roles appends its own profile to
        # whatever is active (tasks/main.yml of fedora.linux_system_roles 1.121.0), so
        # a host it manages runs "virtual-guest kernel_settings". The expected profile
        # is still the one in force, so match without it as well.
        for candidate in list(candidates):
            profiles = [p for p in candidate.split() if p != KERNEL_SETTINGS_PROFILE]
            if profiles and len(profiles) < len(candidate.split()):
                candidates.append(' '.join(profiles))
        suffix = f' (post-loaded: "{post_loaded}")' if post_loaded else ''
        if expected in candidates:
            msg = f'tuned profile is "{active}"{suffix} (as expected).'
            state = STATE_OK
        else:
            msg = (
                f'tuned profile is "{active}"{suffix}, but supposed to be'
                f' "{args.TUNED_PROFILE}".'
            )
            state = STATE_WARN

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


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