# AIX OS parameter collector (SERVER-1794)
#
# Tunables are exposed through vmo / no / ioo / schedo.  Each command prints
# "<key> = <value>" for "-o <key>".  Default keys are managed here in code;
# extra keys can be requested by the user through the custom option.

import platform
import threading
import whatap.util.process_util as process_util
from whatap.osinfo.os_parameter_common import OsParameter, BackgroundCollector

OS_NAME = platform.system() or 'AIX'

# command -> category (parameter nature)
_CMD_CATEGORY = {
    'vmo': 'memory',
    'no': 'network',
    'ioo': 'io',
    'schedo': 'scheduler',
}

# command -> default key list (see aix.sh reference)
_DEFAULT_KEYS = {
    'vmo': [
        'minperm%', 'maxperm%', 'maxclient%', 'strict_maxclient', 'strict_maxperm',
        'maxpin%', 'lru_file_repage', 'page_steal_method', 'v_pinshm',
        'lgpg_regions', 'lgpg_size', 'minfree', 'maxfree', 'memory_affinity',
        'esid_allocator',
    ],
    'no': [
        'tcp_sendspace', 'tcp_recvspace', 'udp_sendspace', 'udp_recvspace',
        'sb_max', 'rfc1323', 'tcp_nodelayack', 'tcp_keepinit', 'tcp_keepidle',
        'tcp_keepintvl', 'tcp_keepcnt', 'tcp_timewait', 'tcp_ephemeral_low',
        'tcp_ephemeral_high', 'udp_ephemeral_low', 'udp_ephemeral_high',
        'somaxconn', 'clean_partial_conns', 'tcp_pmtu_discover',
        'udp_pmtu_discover', 'ipforwarding', 'ipsendredirects',
        'directed_broadcast', 'nonlocsrcroute', 'ipsrcrouteforward',
    ],
    'ioo': [
        'maxpgahead', 'minpgahead', 'j2_maxPageReadAhead',
        'j2_dynamicBufferPreallocation', 'j2_inodeCacheSize',
        'j2_metadataCacheSize', 'maxrandwrt', 'numfsbufs', 'pv_min_pbuf',
        'maxreqs', 'aio_maxreqs', 'aio_maxservers', 'aio_minservers',
        'posix_aio_maxreqs', 'posix_aio_maxservers',
    ],
    'schedo': [
        'timeslice', 'vpm_xvcpus', 'smt_snooze_delay', 'affinity_lim',
        'idle_migration_barrier',
    ],
}

_CMD_ORDER = ['vmo', 'no', 'ioo', 'schedo']

_custom_params = []
_custom_lock = threading.Lock()


def SetCustomParams(params):
    global _custom_params
    with _custom_lock:
        _custom_params = list(params or [])


def _get_custom_params():
    with _custom_lock:
        return list(_custom_params)


def _query(cmd, key):
    """Return the value string for ``<cmd> -o <key>`` or None."""
    try:
        out = process_util.executeCommandShellWithTimeout(
            '%s -o %s 2>/dev/null' % (cmd, key), timeout=10)
    except Exception:
        return None
    if not out:
        return None
    for line in out.split('\n'):
        if '=' not in line:
            continue
        left, right = line.split('=', 1)
        if left.strip() == key:
            value = right.strip().replace(',', ' ')
            if value:
                return value
    return None


def _collect():
    result = []
    seen = set()

    for cmd in _CMD_ORDER:
        category = _CMD_CATEGORY[cmd]
        for key in _DEFAULT_KEYS[cmd]:
            value = _query(cmd, key)
            if value is None:
                continue
            result.append(OsParameter(key, category, OS_NAME, value, custom=False))
            seen.add((key, category))

    for param, category_hint in _get_custom_params():
        value = None
        found_category = None
        for cmd in _CMD_ORDER:
            value = _query(cmd, param)
            if value is not None:
                found_category = _CMD_CATEGORY[cmd]
                break
        if value is None:
            continue
        category = category_hint or found_category or 'custom'
        if (param, category) in seen:
            continue
        result.append(OsParameter(param, category, OS_NAME, value, custom=True))
        seen.add((param, category))

    return result


_collector = BackgroundCollector('os_parameter_aix', _collect, interval=300)


def GetOsParameterList(custom_params=None):
    if custom_params is not None:
        SetCustomParams(custom_params)
    return _collector.get()
