#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""confscan - Linux system configuration collector.

Declarative whitelist-driven collector. Python does scheduling,
accounting and serialization; shell (coreutils) does the raw reads.

Entry point installed as `sysak confscan`.
"""

# the RPM %install stage byte-compiles with python2; this keeps print()
# parseable there (no-op under python3, which is what actually runs)
from __future__ import print_function

import argparse
import os
import sys

# Tool is installed flat into the sysak tools dir; make sibling
# package confscan_lib importable when run from there.
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))

from confscan_lib.config import load_config, ConfigError
from confscan_lib.runner import Runner, TIER_BUDGET
from confscan_lib.report import write_report, DEFAULT_MAX_OUTPUT_BYTES

DEFAULT_CONF_DIR = os.path.join(
    os.path.dirname(os.path.realpath(__file__)), "confscan_conf")
DEFAULT_OUTPUT = "/var/log/sysak/confscan/snapshot.json"


def parse_args():
    parser = argparse.ArgumentParser(
        prog="sysak confscan",
        description="Collect system configuration snapshot as JSON.")
    parser.add_argument("-t", "--tier", default="T1",
                        choices=["T1", "T2", "T3", "all"],
                        help="collection tier (default: T1)")
    parser.add_argument("-d", "--domain", action="append", default=None,
                        metavar="DOM",
                        help="only collect given domain(s), e.g. -d D1 -d D5")
    parser.add_argument("-k", "--key", action="append", default=None,
                        metavar="KEY",
                        help="collect only the given item key(s), e.g. "
                             "-k proc_sys_vm_swappiness; overrides "
                             "--tier/--domain and also picks up items "
                             "disabled for bulk scans")
    parser.add_argument("-o", "--output", default=DEFAULT_OUTPUT,
                        metavar="FILE",
                        help="output file, '-' for stdout "
                             "(default: %s)" % DEFAULT_OUTPUT)
    parser.add_argument("-c", "--conf-dir", default=DEFAULT_CONF_DIR,
                        help=argparse.SUPPRESS)
    parser.add_argument("--max-bytes", type=int, default=DEFAULT_MAX_OUTPUT_BYTES,
                        metavar="N",
                        help="overall output soft cap in bytes; largest "
                             "values dropped (digest kept) when exceeded "
                             "(default: %d)" % DEFAULT_MAX_OUTPUT_BYTES)
    parser.add_argument("-l", "--list", action="store_true",
                        help="list configured collection items and exit")
    return parser.parse_args()


def main():
    args = parse_args()

    try:
        items = load_config(args.conf_dir, tier=args.tier,
                            domains=args.domain, keys=args.key)
    except ConfigError as e:
        print("confscan: %s" % e, file=sys.stderr)
        return 1

    if args.list:
        for item in items:
            print("%-40s %-4s %-6s %s" % (
                item.key, item.domain, item.type, item.source))
        return 0

    if args.key:
        # a named request must not be cut short by the routine tier
        # budget: -k on a T3 command under the default T1 budget would
        # otherwise be killed before it finishes
        runner = Runner(items, tier="key", budget=TIER_BUDGET["all"])
    else:
        runner = Runner(items, tier=args.tier)
    result = runner.run()

    write_report(result, args.output, max_bytes=args.max_bytes)
    return 0


if __name__ == "__main__":
    sys.exit(main())
