#!/usr/bin/python3
"""Diagnostic program for the limit system

troubleshoot-limits [ OPTIONS ] [ TASK-FILE ]

TASK-FILE is the path to a file containing the task you wish to test.
The easy ways to obtain one of these is to use the 'task' command's
--export switch or use cURL to export an existing task from the server
using its URL.  If no TASK-FILE is provided and the standard input is
a TTY, a default 'idle' task will be used.  If none is provided and
the standrd input is not a TTY, the task will be read from the
standard input.

Options:

--config CONFIG-FILE - Location of the limit configuration file.
  Defaults to the limit configuration file on the local system.

--hints HINTS - JSON hints (or @/path to read from a file).  If not
  provided, hints of a local loopback interface will be used for
  the requester and server.

--start START-TIME - Proposed start time as an ISO 8601 timestamp.  If
  not provided, time-related limits will be ignored.  If not provided
  but --duration is, the current date and time will be used.

--duration DURATION - How long the proposed task is supposed to take
  on the timeline in ISO8601 format.  If not provided but --start is,
  a default of PT10S will be used.

"""


import optparse
import pscheduler
import sys

from pscheduler.limitprocessor.limitprocessor import LimitProcessor

pscheduler.set_graceful_exit()

#
# Gargle the arguments
#


class VerbatimParser(optparse.OptionParser):
    def format_epilog(self, formatter):
        return self.epilog

opt_parser = VerbatimParser(
    usage="Usage: %prog [ OPTIONS ] TASK-FILE",
    epilog=
"""

If no TASK-FILE is provided

Example:

  limit-diags taskfile
      Test the task described in taskfile against the system's limits.

  limit-diags --start 2017-08-14T12:34:56 --duration PT10S taskfile
      Test the task described in taskfile against the system's limits for
      a specified time and duration.
"""
    )


# TODO: This needs to be filled in during build
default_limit_file = "/etc/pscheduler/limits.conf"


# Task used if no file is specified.
DEFAULT_TASK = {
    "test": {
        "type": "idle",
        "spec": {
            "schema": 1,
            "duration": "PT2S"
        }
    }
}



opt_parser.disable_interspersed_args()

opt_parser.add_option("--duration",
                      help="Proposed duration (ISO 8601)",
                      default=None,
                      action="store", type="string",
                      dest="duration")

opt_parser.add_option("--hints",
                      help="Hints as JSON (@/path to read from file;"
                      " defaults to a loopback address for all values)",
                      default=None,
                      action="store", type="string",
                      dest="hints")

opt_parser.add_option("--config",
                      help="Location of limit configuration file (default %s)" \
                      % (default_limit_file),
                      default=default_limit_file,
                      action="store", type="string",
                      dest="config")

opt_parser.add_option("--start",
                      help="Proposed start time (ISO 8601)",
                      default=None,
                      action="store", type="string",
                      dest="start")


(options, remaining_args) = opt_parser.parse_args()


#
# Validate everything
#

try:
    if options.start is not None:
        _ = pscheduler.iso8601_as_datetime(options.start)
except ValueError as ex:
    pscheduler.fail("Start time: %s" % (str(ex)))

try:
    if options.duration is not None:
        _ = pscheduler.iso8601_as_timedelta(options.duration)
except ValueError as ex:
    pscheduler.fail("Duration: %s" % (str(ex)))

if options.hints is not None:
    try:
        hints = pscheduler.json_load(
            pscheduler.string_from_file(options.hints))
    except (IOError, ValueError) as ex:
        pscheduler.fail("Hints: %s" % (str(ex)))
else:
    try:
        loopback = str(pscheduler.LocalIPList().loopback())
    except IndexError:
        # No loopbacks.  Horse 'em.
        loopback = '::1'
    hints = {
        "requester": loopback,
        "server": loopback
    }

if len(remaining_args) not in [0, 1]:
    opt_parser.print_usage()
    pscheduler.fail()



try:
    processor = LimitProcessor(options.config)
except Exception as ex:
    pscheduler.fail("Unable to read limit configuration: %s" % (str(ex)))



if remaining_args or not sys.stdin.isatty():
    try:
        task_file = remaining_args[0]
        task_text = pscheduler.string_from_file("@%s" % (task_file))
    except IndexError:
        # No argument; read from stdin
        task_text = sys.stdin.read()
        
    try:
        task = pscheduler.json_load(task_text)
    except ValueError as ex:
        pscheduler.fail("Unable to read task: %s" % (str(ex)))
elif sys.stdin.isatty():
    print("Using built-in default task.")
    task = DEFAULT_TASK
else:
    opt_parser.print_usage()
    pscheduler.fail()



if options.start is not None:
    task["run_schedule"] = {
        "start": options.start
    }
    task["run_schedule"]["duration"] = options.duration \
                                   if options.duration is not None else "PT10S"
        
elif options.duration is not None:
    task["run_schedule"] = {
        "duration": options.duration
    }
    task["run_schedule"]["start"] = pscheduler.datetime_as_iso8601(
        pscheduler.time_now())

if "run_schedule" in task:
    print(("For task starting at %s lasting %s:\n" % (
        task["run_schedule"]["start"], task["run_schedule"]["duration"])))

_passed, diags, new_task, priority \
    = processor.process(task, hints, prioritize=True)

print(diags)
if new_task is not None:
    print()
    print("Rewritten task:")
    print((pscheduler.json_dump(new_task, pretty=True)))

pscheduler.succeed()

exit(99)
