Advertisement
Guest User

Untitled

a guest
Apr 13th, 2023
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.15 KB | None | 0 0
  1. #!/usr/bin/python3
  2. # -*- coding: utf-8 -*-
  3.  
  4. """ See if the HD Homerun box(s) are accessible and running
  5.  
  6. Requires Python 3.6 or later.
  7.  
  8. For backends started by systemd, use:
  9.  
  10. sudo --preserve-env systemctl edit --force mythtv-backend.service
  11.  
  12. and enter or add as needed by your service:
  13.  
  14. [Service]
  15. ExecStartPre=-/usr/local/bin/hdhomerun_check.py
  16.  
  17. Can be called with optional IP address(s) for users that
  18. have multiple HDHRs that have STATIC addresses.
  19.  
  20. Use --help to see all options.
  21.  
  22. If run from the command line, then output will be to the screen.
  23. Otherwise, a log file in /tmp named hdhr_discovery.log is made.
  24. Changable with the --logfile switch.
  25.  
  26. Exit codes:
  27.  
  28. 0 = success (for *ALL* HDHRs if multiple IPs were specified)
  29. 1 = no output from the hdhomerun_config discover command
  30. 2 = IPv4 and IPv6 addresses found, disable IPv6 on NIC
  31. 3 = logfile isn't writable, delete it and try again
  32. 4 = keyboard interrupt
  33. 5 x the number of HDHRs = HDHR is most likely not up
  34.  
  35. """
  36.  
  37. __version__ = '1.28'
  38.  
  39. import argparse
  40. import signal
  41. import subprocess
  42. import sys
  43. from datetime import datetime
  44. from os.path import basename
  45. from os import _exit
  46. from time import sleep
  47.  
  48.  
  49. # pylint: disable=too-many-arguments,unused-argument
  50. def keyboard_interrupt_handler(sigint, frame):
  51. ''' Handle all KeyboardInterrupts here. And, just leave. '''
  52. _exit(4)
  53. # pylint: enable=unused-argument
  54.  
  55.  
  56. def get_program_arguments():
  57. ''' Process the command line. '''
  58.  
  59. parser = argparse.ArgumentParser(description='HDHR Access Test',
  60. epilog='* Default values are in ()s')
  61.  
  62. parser.add_argument('HOSTS', type=str, default=None, nargs='*',
  63. help='optional hostname(s)/IP(s) (%(default)s)')
  64.  
  65. parser.add_argument('--attempts', default=20, type=int, metavar='<num>',
  66. help='number of tries to find HDHRs (%(default)i)')
  67.  
  68. parser.add_argument('--debug', action='store_true',
  69. help='output additional information (%(default)s)')
  70.  
  71. parser.add_argument('--logfile', default='/tmp/hdhomerun_check.log',
  72. type=str, metavar='<lf>',
  73. help='optional path + name of log file (%(default)s)')
  74.  
  75. parser.add_argument('--sleep', default=1.5, type=float, metavar='<sec>',
  76. help='seconds betweem attempts (%(default)s)')
  77.  
  78. parser.add_argument('--version', action='version',
  79. version='%(prog)s ' + __version__)
  80.  
  81. return parser.parse_args()
  82.  
  83.  
  84. def get_elapsed_time(start):
  85. ''' Calculate the time spent waiting for the HDHR to come up. '''
  86.  
  87. delta = datetime.utcnow() - start
  88. rounded_delta = f'{delta.seconds + (delta.microseconds / 1000000):.3f}'
  89. return rounded_delta
  90.  
  91.  
  92. def log_or_print(loglevel, message, output):
  93. ''' Add timestamp, log level then print to the selected location. '''
  94.  
  95. print(datetime.now().strftime("%F %T.%f")[:-3], f'{loglevel:8}', message,
  96. file=output)
  97.  
  98.  
  99. def last_message(loglevel, result, host, start, attempt, output):
  100. ''' Common success or failure message text. '''
  101.  
  102. log_or_print(loglevel, f'{result} {"at " + host + " " if host else ""}'
  103. f'in {get_elapsed_time(start)} seconds '
  104. f'and {attempt} attempt{"s"[attempt == 1:]}\n', output)
  105.  
  106.  
  107. def check_one_device(host, args, output):
  108. ''' Try to discover the HDHR(s). '''
  109.  
  110. attempt = 0
  111. command = ['hdhomerun_config', 'discover']
  112. start = datetime.utcnow()
  113.  
  114. if host:
  115. command.append(host)
  116.  
  117. for attempt in range(1, args.attempts+1):
  118.  
  119. try:
  120. discovery_response = subprocess.check_output(
  121. command, text=True, stderr=subprocess.STDOUT).split()
  122. except subprocess.CalledProcessError:
  123. log_or_print('WARNING', f'{command[0]}: got no response, attempt: '
  124. f'{attempt:2}', output)
  125. sleep(args.sleep)
  126. continue
  127.  
  128. if not discovery_response:
  129. log_or_print('ERROR', f'No output from {command[0]}, aborting!',
  130. output)
  131. sys.exit(1)
  132.  
  133. if args.debug:
  134. log_or_print('DEBUG', f'Got: {" ".join(discovery_response)}',
  135. output)
  136.  
  137. if discovery_response.count('hdhomerun') > 1:
  138. log_or_print('ERROR', f'{command[0]}: more than 1 IP, aborting!',
  139. output)
  140. sys.exit(2)
  141.  
  142. if discovery_response[0] != 'hdhomerun':
  143. # Consider making this an ERROR and exiting not sleeping...
  144. log_or_print('WARNING', f'{command[0]} got an unexpected response:'
  145. f' {" ".join(discovery_response)}',
  146. output)
  147. sleep(args.sleep)
  148. else:
  149. last_message('INFO', f'Found HDHR {discovery_response[2]}', host,
  150. start, attempt, output)
  151. return 0
  152.  
  153. last_message('ERROR', 'No HDHR found', host, start, attempt, output)
  154.  
  155. return 5
  156.  
  157.  
  158. def main(args, output=None):
  159. ''' Control checking of one or more devices. '''
  160.  
  161. log_or_print('INFO', f'Starting {basename(__file__)} v{__version__}, '
  162. f'attempts={args.attempts}, sleep={args.sleep:.2f}', output)
  163.  
  164. if args.HOSTS:
  165.  
  166. return_value = 0
  167.  
  168. for host in args.HOSTS:
  169. return_value += check_one_device(host, args, output)
  170.  
  171. else:
  172. return_value = check_one_device(None, args, output)
  173.  
  174. return return_value
  175.  
  176.  
  177. if __name__ == '__main__':
  178.  
  179. signal.signal(signal.SIGINT, keyboard_interrupt_handler)
  180.  
  181. ARGS = get_program_arguments()
  182.  
  183. if sys.stdin and sys.stdin.isatty():
  184. RETURN_VALUE = main(ARGS)
  185. else:
  186. try:
  187. with open(ARGS.logfile, encoding='ascii', mode='a') as file_obj:
  188. RETURN_VALUE = main(ARGS, output=file_obj)
  189. except PermissionError:
  190. print(f'Can\'t write to {ARGS.logfile}, aborting!')
  191. sys.exit(3)
  192.  
  193. sys.exit(RETURN_VALUE)
  194.  
  195. # vim: set expandtab tabstop=4 shiftwidth=4 smartindent colorcolumn=80:
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement