#!/usr/bin/python3
"""Command-line interface to Pacemaker's Cluster Test Suite (CTS)."""

# pylint doesn't like the module name "cts-lab" which is an invalid complaint for this file
# This also disables various other invalid names - it thinks scenario and match are constants
# that should have all caps names, and that cm and n are too short.
# pylint: disable=invalid-name

__copyright__ = "Copyright 2001-2026 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"

from functools import partial
import signal
import sys

from pacemaker._cts.CTS import CtsLab
from pacemaker._cts.audits import audit_list
from pacemaker._cts.cmcorosync import Corosync2
from pacemaker._cts.environment import Environment
from pacemaker._cts import logging
from pacemaker._cts.scenarios import AllOnce, Boot, BootCluster, LeaveBooted, RandomTests, Sequence
from pacemaker._cts.tests import test_list


def term_handler(scenario, signum, _frame):
    """Handle a SIGTERM by exiting gracefully."""
    logging.log(f"Interrupted by signal {signum}")

    if scenario:
        scenario.summarize()
        scenario.teardown()

    sys.exit(1)


def plural_s(n):
    """Return a string suffix depending on whether or not n is > 1."""
    return "" if n == 1 else "S"


def main():
    """Run Pacemaker CTS lab as specified by arguments."""
    logging.add_stderr()

    env = Environment(sys.argv[1:])
    lab = CtsLab(env)
    iters = lab["iterations"]

    # Create the Cluster Manager object.
    # Currently Corosync2 is the only available cluster manager.
    cm = Corosync2(env)

    if lab["TruncateLog"]:
        if lab["OutputFile"] is None:
            logging.log("Ignoring truncate request because no output file specified")
        else:
            logging.log(f"Truncating {lab['OutputFile']}")

            with open(lab["OutputFile"], "w", encoding="utf-8") as outputfile:
                outputfile.truncate(0)

    audits = audit_list(cm)
    tests = test_list(cm, env, audits)

    if lab["ListTests"]:
        logging.log(f"Total {len(tests)} tests")

        for test in tests:
            logging.log(test.name)

        sys.exit(0)

    if lab["tests"]:
        # Get tests corresponding to the names in lab["tests"]
        test_map = {test.name: test for test in tests}

        try:
            tests = [test_map[test] for test in lab["tests"]]

        except KeyError as e:
            logging.log(f"--choose: Test {e} is invalid or not applicable")
            sys.exit(1)

    # Scenario selection
    if lab["scenario"] == "all-once":
        iters = len(tests)
        scenario = AllOnce(cm, [BootCluster(cm)], audits, tests)
    elif lab["scenario"] == "sequence":
        scenario = Sequence(cm, [BootCluster(cm)], audits, tests)
    elif lab["scenario"] == "boot":
        scenario = Boot(cm, [LeaveBooted(cm)], audits, [])
    else:
        scenario = RandomTests(cm, [BootCluster(cm)], audits, tests)

    logging.log(f">>>>>>>>>>>>>>>> BEGINNING {iters!r} TEST{plural_s(iters)}")
    logging.log(f"Schema:                 {lab['Schema']}")
    logging.log(f"Scenario:               {scenario.__doc__}")
    logging.log(f"CTS Exerciser:          {lab['cts-exerciser']}")
    logging.log(f"CTS Logfile:            {lab['OutputFile']}")

    if "syslogd" in lab:
        logging.log(f"Syslog variant:         {lab['syslogd']}")

    logging.log(f"System log files:       {lab['LogFileName']}")

    if "IPBase" in lab:
        logging.log(f"Base IP for resources:  {lab['IPBase']}")

    logging.log(f"Cluster starts at boot: {lab['at-boot']}")

    lab.dump()

    # On SIGTERM during lab run, print a summary and do teardown
    signal.signal(15, partial(term_handler, scenario))

    rc = lab.run(scenario, iters)
    sys.exit(rc)


if __name__ == '__main__':
    main()

# vim: set filetype=python:
