#!/usr/bin/env python3

"""Check clocks for a list of hosts."""

import sys
import time
import pprint

sys.path.insert(0, '/usr/local/lib')

import deep_ssh  # noqa: disable=E402


def usage(retval):
    """Output a usage message."""
    if retval in (0, None):
        write = sys.stdout.write
    else:
        write = sys.stderr.write

    write('Usage: {} --tolerance 2.5 --verbose --hostpaths foo.example.com bar.example.com\n'.format(sys.argv[0]))
    write('\n')
    write('--hostpaths must be the last option specified.\n')
    write('--hostpaths may include:\n')
    write('   usernames, like root@baz.example.com\n')
    write('   bang paths like foo.example.com!bar.example.com\n')
    write('   ports like bar.example.com%2222\n')
    write('   ...or mixes of the above, like root@foo.eg.com%222!user@blee.eg.com%223!someone@bar.eg.com%2222\n')

    sys.exit(retval)


def main():
    """Check clocks - main function."""
    hostpaths = []
    tolerance = 2.5
    verbose = False

    while sys.argv[1:]:
        if sys.argv[1] == '--hostpaths':
            # must be last option
            hostpaths = sys.argv[2:]
            break
        elif sys.argv[1] == '--tolerance':
            tolerance = float(sys.argv[2])
            del sys.argv[1]
        elif sys.argv[1] == '--verbose':
            verbose = True
        elif sys.argv[1] in ('-h', '--help'):
            usage(0)
        else:
            sys.stderr.write('Unrecognized option: {}\n'.format(sys.argv[1]))
            usage(1)
        del sys.argv[1]

    if not hostpaths:
        sys.stderr.write('{}: --hostpaths is a required option\n'.format(sys.argv[0]))
        usage(1)

    all_good = True

    hostpath_time_dict = {}
    for hostpath in hostpaths:
        shell_command = 'python3 -c "import time; print(time.time())"'
        popen_result = deep_ssh.handle(
                optional_opts='',
                chain=hostpath,
                command=shell_command,
                popen=True,
            )
        lines = popen_result.readlines()
        exit_code = popen_result.close()
        if exit_code not in (0, None):
            sys.stderr.write('{}: bad exit code for {}: {}\n'.format(sys.argv[0], hostpath, exit_code))
        if len(lines) != 1:
            sys.stderr.write('{}: bad number of lines of output for {}: {}\n'.format(sys.argv[0], hostpath, len(lines)))

        str_time = lines[0].rstrip('\n')

        hostpath_time_dict[hostpath] = float(str_time)

    if verbose:
        pprint.pprint(hostpath_time_dict)

    mean = sum(hostpath_time_dict.values()) / len(hostpath_time_dict)

    for hostname, timeval in hostpath_time_dict.items():
        if abs(mean - timeval) > tolerance:
            sys.stderr.write('{} ({}, {}) too far from mean ({})\n'.format(hostname, timeval, time.ctime(timeval), mean))
            all_good = False

    if not all_good:
        sys.stderr.write('One or more clocks too far off\n')
        sys.exit(1)


main()