#!/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()