#!/usr/bin/python -u

"""
This program does very basic troubleshooting of one or two systems
running pScheduler, veryfying that the service is available and
running several tests to verify that the basic features of the system
are working.
"""

import optparse
import os
import pipes
import sys
import time

import pscheduler


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 ] host",
    epilog=

"""
Examples:

  troubleshoot
      Troubleshoot the local system

  troubleshoot --host ps.example.com
      Troubleshoot ps.example.com

  troubleshoot ps2.example.com
      Troubleshoot the local host and ps2.example.com

  troubleshoot --host ps.example.com ps2.example.com
      Troubleshoot ps.example.com and ps2.example.com
"""
    )
opt_parser.disable_interspersed_args()

opt_parser.add_option("--host",
                      help="Base host for troubleshooting",
                      default="localhost",
                      action="store", type="string",
                      dest="host")

(options, remaining_args) = opt_parser.parse_args()

if len(remaining_args) > 1:
    opt_parser.print_usage()
    pscheduler.fail()


host_a = options.host
hosts = [host_a]
try:
    host_z = remaining_args[0]
    hosts.append(host_z)
except IndexError:
    host_z = None



# TODO: This would be a nice "narrator" class.

def start(text):
    sys.stdout.write("%s..." % (text))

def progress(text=None):
    if text is None:
        sys.stdout.write(".")
    else:
        start(" %s" % (text))

def ok():
    sys.stdout.write(" OK.\n")

def failed(why, fail=True):
    sys.stdout.write(" Failed.\n")
    print pscheduler.indent(why)
    if fail:
        pscheduler.fail()




def run_task(message, lead, task, bind=None):
    """
    Run a task and return the result.
    """
    start(message)

    # Post

    tasks_url = pscheduler.api_url(lead, '/tasks')
    try:
        status, task_url = pscheduler.url_post(
            tasks_url,
            data=pscheduler.json_dump(task),
            bind=bind,
            throw=True)
    except Exception as ex:
        failed("Unable to post task: %s" % (str(ex)))

    if status != 200:
        failed("Failed to post task: " + task_url)

    # Fetch posted task

    status, task_data = pscheduler.url_get("%s?detail" % (task_url))
    if status != 200:
        failed("Failed to post task: " + task_data)

    try:
        first_run_url = task_data["detail"]["first-run-href"]
    except KeyError:
        failed("Server returned incomplete data.")

    progress()


    # Get first run and make sure we have what we need to function.

    status, run_data = pscheduler.url_get(first_run_url, throw=False)

    if status == 404:
        failed("The server never scheduled a run for the task.")
    if status != 200:
        failed("Error %d: %s" % (status, run_data))

    for key in ["end-time", "result-href"]:
        if key not in run_data:
            failed("Server did not return %s with run data" % (key))

    # Wait for the end time to pass

    try:
        end_time = pscheduler.iso8601_as_datetime(run_data["end-time"])
    except ValueError as ex:
        failed("Server did not return a valid end time for the task: %s" % (str(ex)))

    sleep_time = pscheduler.time_until_seconds(end_time)

    progress("%d seconds" % (sleep_time))
    time.sleep(sleep_time)

    # Wait for the result to happen

    status, run_data = pscheduler.url_get(first_run_url,
                                          params={"wait-merged": True},
                                          throw=False)

    if status != 200:
        failed("Error %d: %s" % (status, run_data))


    # Get the result

    status, result_data = pscheduler.url_get(run_data["result-href"],
                                             params={"wait-merged": True},
                                             throw=False)
    if status != 200:
        print status, result_data
        failed("Did not get a result: %s" % (result_data))
    progress()

    try:
        if not result_data["succeeded"]:
            failed("Test failed to run properly.", fail=False)
            result_args = ["pscheduler", "result", first_run_url]
            os.execl("/bin/sh", "/bin/sh", "-c",
                     " ".join([pipes.quote(arg) for arg in result_args]))
    except KeyError:
        failed("Server didn't return the expected result.")

    ok()
    return result_data



print "Performing basic troubleshooting of %s." \
    % (" and ".join(hosts))
print


#
# The Basics
#

for host in hosts:
    start("Checking for pScheduler on %s" % (host))
    up, reason = pscheduler.api_ping(host)
    if up:
        ok()
    else:
        failed(reason)



for host in hosts:
    run_task("Idle test on %s" % (host), host,
             {
                 "schema": 1,
                 "test": {
                     "spec": {
                         "duration": "PT1S",
                         "schema": 1
                     },
                     "type": "idle"
                 },
                 "schedule": {}
             }
            )



#
# Remote pScheduler
#

if host_z is None:
    print
    pscheduler.succeed("pScheduler appears to be functioning normally.")


# Check clocks

clock_result = run_task(
    "Getting host clocks", host_a,
    {
        "schema": 1,
        "test": {
            "spec": {
                "dest": "perfsonardev0.internet2.edu",
                "schema": 1
            },
            "type": "clock"
        },
        "schedule": {}
    }
    )

start("Comparing clocks")
try:
    clock_difference = pscheduler.timedelta_as_seconds(
        pscheduler.iso8601_as_timedelta(clock_result["difference"])
    )
except (KeyError, ValueError):
    failed("Clock test did not yield a valid result.")

if clock_difference > 1.0:
    failed("Clocks differ between hosts by %f seconds" % (clock_difference))
ok()


# Simplestream

simplestream_result = run_task(
    "Simple stream test", host_a,
    {
        "schema": 1,
        "test": {
            "spec": {
                "dest": host_z,
                "schema": 1
            },
            "type": "simplestream"
        },
        "schedule": {}
    }
    )


#
# The End.
#

print
pscheduler.succeed(
    "pScheduler on both hosts appears to be functioning normally.")
