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

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

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

DESCRIPTION = """Displays system-wide Podman information including container counts, image count,
storage driver, runtime version, available CPUs, and total memory. Also monitors the
Podman daemon stderr for warnings and errors. Individual stderr lines can be filtered
out with --ignore (e.g. benign cgroup warnings on rootless hosts). For Docker, use the
docker-info check instead.
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(
        '--always-ok',
        help=lib.args.help('--always-ok'),
        dest='ALWAYS_OK',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '--ignore',
        help='Ignore stderr lines matching this Python regular expression. '
        'Case-sensitive by default; use `(?i)` for case-insensitive matching. '
        'Can be specified multiple times. '
        'Example: `--ignore="cgroup v1"` to suppress a benign cgroup-version '
        'warning on hosts that have not yet migrated to cgroup v2. '
        'Example: `--ignore="(?i)rootless"` (case-insensitive) to suppress '
        'any rootless-related informational warning. '
        'Default: %(default)s',
        dest='IGNORE',
        action='append',
        default=None,
    )

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

    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)

    if args.IGNORE is None:
        args.IGNORE = []

    # compile ignore patterns (case-sensitive by default, matching the
    # lib.args convention for --match / --ignore-regex; the user can
    # opt into case-insensitive matching with the inline `(?i)` flag).
    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:
        stdout, stderr, retc = lib.base.coe(
            lib.shell.shell_exec('podman info --format json'),
        )
        if retc != 0:
            lib.base.oao(f'{stderr}\n{stdout}', STATE_CRIT)
        try:
            result = json.loads(stdout)
        except Exception:
            lib.base.cu('Unable to parse podman info output as JSON.')
    else:
        # do not call the command, put in test data
        stdout, stderr, retc = lib.lftest.test(args.TEST)
        try:
            result = json.loads(stdout)
        except Exception:
            lib.base.cu('Unable to parse podman info output as JSON.')

    # init some vars
    msg = ''
    state = STATE_OK
    perfdata = ''
    containers = containers_paused = containers_running = ''
    containers_stopped = cpus = images = logging_driver = ''
    memory_human = registry = storage_driver = ver = ''
    warn, crit = '', ''

    # analyze data - extract values from podman info JSON output
    containers = result['store']['containerStore']['number']
    containers_paused = result['store']['containerStore']['paused']
    containers_running = result['store']['containerStore']['running']
    containers_stopped = result['store']['containerStore']['stopped']
    cpus = result['host']['cpus']
    images = result['store']['imageStore']['number']
    logging_driver = result['host']['eventLogger']
    memory_human = lib.human.bytes2human(result['host']['memTotal'])
    num_registries = len(result['registries']['search'])
    registry = (
        f'{num_registries} {lib.txt.pluralize("Registr", num_registries, "y,ies")}'
    )
    storage_driver = result['store']['graphDriverName']
    ver = result['version']['Version']

    # check stderr for warnings and errors, skipping lines matched by
    # --ignore (#834: same treatment as docker-info so admins can
    # suppress boilerplate stderr warnings that cannot be silenced in
    # the daemon config).
    for row in stderr.strip().split('\n'):
        if any(pattern.search(row) for pattern in ignore_patterns):
            continue
        lcrow = row.lower()
        if 'warning: ' in lcrow:
            warn += f'{row}, '
            state = lib.base.get_worst(state, STATE_WARN)
        if 'error: ' in lcrow:
            crit += f'{row}, '
            state = lib.base.get_worst(state, STATE_CRIT)

    # build perfdata
    perfdata += lib.base.get_perfdata(
        'containers',
        containers,
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'containers_paused',
        containers_paused,
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'containers_running',
        containers_running,
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'containers_stopped',
        containers_stopped,
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'cpu',
        cpus,
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'images',
        images,
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'ram',
        result['host']['memTotal'],
        uom='B',
        _min=0,
    )

    # create output
    if crit:

        # build the message
        msg += f'{crit}'
    if warn:
        msg += f'{warn}'
    if containers != '':
        msg += f'{containers} Containers'
    if containers_running != '':
        msg += (
            f' ({containers_running} running,'
            f' {containers_paused} paused,'
            f' {containers_stopped} stopped)'
        )
    if images != '':
        msg += f', {images} Images'
    if storage_driver != '':
        msg += f', Storage Driver: {storage_driver}'
    if logging_driver != '':
        msg += f', Logging Driver: {logging_driver}'
    if registry != '':
        msg += f', Registry: {registry}'
    msg += f', Podman v{ver}'
    msg += f', {cpus} CPUs'
    msg += f', {memory_human} Memory'

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


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