be66a7caed9ebc11e6f30cf9a216047e29a5e4c3
David Blume first commit.

David Blume authored 5 years ago

1) #!/usr/bin/python3
2) #
3) # Since this is meant to be run as a daemon, do not use #!/usr/bin/env python3,
4) # because the process name would then be python3, not based on __FILE__.
5) #
6) # [filename] -d 3 192.168.1.1 192.168.1.12 &
7) # disown [jobid]
8) import os
9) import sys
10) import subprocess
11) import time
12) from argparse import ArgumentParser
13) import threading
14) import queue
David Blume Add notifications for SIGTE...

David Blume authored 5 years ago

15) import signal
David Blume first commit.

David Blume authored 5 years ago

16) 
David Blume Serialize the reading and w...

David Blume authored 5 years ago

17) pinger_lock = threading.Lock()
18) 
David Blume first commit.

David Blume authored 5 years ago

19) 
David Blume Rename a parameter instead...

David Blume authored 5 years ago

20) def log_exit(sig, frame):
21)     _print_queue.put([f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} '
22)                       f'PID={os.getpid()} signal={signal.Signals(sig).name} Exiting.'])
David Blume Add notifications for SIGTE...

David Blume authored 5 years ago

23)     _print_queue.join()
24)     sys.exit(0)
25) 
26) 
David Blume first commit.

David Blume authored 5 years ago

27) def pinger():
28)     """Each queue item is a new address to ping. Ping and compare and print.
29)     Issue: Each of these threads reads from and writes to the global _results dict.
30)     """
31)     while True:
32)         host = _ping_queue.get()
33)         now = time.localtime()
34)         success = ping(host)
David Blume Serialize the reading and w...

David Blume authored 5 years ago

35)         with pinger_lock:
36)             # Access to _results needs to be serialized
37)             need_update = success != _results[host]
38)             if need_update:
39)                 _results[host] = success
40)         if need_update:
David Blume first commit.

David Blume authored 5 years ago

41)             status = "UP" if success else "DOWN"
42)             _print_queue.put([f'{time.strftime("%Y-%m-%d %H:%M:%S", now)} {host} {status}'])
43)         _ping_queue.task_done()
44) 
45) 
46) def ping(host):
47)     """Returns True if host (str) responds to a ping request."""
48)     return subprocess.call([*_ping_cmd, host], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
49) 
50) 
51) def print_manager():
52)     """The only thread allowed to write output."""
53)     while True:
54)         job = _print_queue.get()
55)         for line in job:
56)             log(line)
57)         _print_queue.task_done()
58) 
59) 
60) def log(*args, **kwargs):
61)     """Opens the logfile, writes the log, and closes the logfile."""
62)     # I don't leave the log file open for writing because another process needs access too.
63)     with open(_logname, 'a') as fp:
64)         return print(*args, **kwargs, file=fp)
65) 
66) 
67) if __name__ == '__main__':
68)     parser = ArgumentParser(description="Repeatedly ping sites. Ex: %(prog)s -d 3 192.168.1.1 192.168.1.12")
69)     parser.add_argument('-d', '--delay', type=float, default=1.0, help='Delay between pings')
70)     parser.add_argument('addresses', nargs='+', help='Addresses to ping')
71)     parser_args = parser.parse_args()
72) 
73)     _results = dict()
74)     for addr in parser_args.addresses:
75)         _results[addr] = None
76)     delay = parser_args.delay
77) 
78)     _ping_queue = queue.Queue(2 * len(parser_args.addresses))  # No more than 2 pending pings per address
79)     _print_queue = queue.Queue()
80) 
81)     _logname = os.path.join(os.path.expanduser('~'), 'log', os.path.basename(sys.argv[0]).replace('py', 'txt'))
82)     _pid = os.getpid()
83) 
84)     # Choose the ping parameters appropriate for the platform
85)     if sys.platform == 'cygwin' or sys.platform == 'win32':
86)         _ping_cmd = ('ping', '-n', '1', '-w', '2000')
87)     else:
88)         _ping_cmd = ('ping', '-c', '1', '-W', '2', '-q')
89) 
90)     for i in range(len(parser_args.addresses)):
91)         t = threading.Thread(target=pinger, daemon=True)
92)         t.start()
93)         del t
94) 
95)     t = threading.Thread(target=print_manager, daemon=True)
96)     t.start()
97)     del t
98) 
99)     _print_queue.put([f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}'
David Blume Add notifications for SIGTE...

David Blume authored 5 years ago

100)         f' PID={os.getpid()} repeat={delay}s addresses={",".join(parser_args.addresses)} Starting'])
101) 
102)     signal.signal(signal.SIGINT, log_exit)
103)     signal.signal(signal.SIGTERM, log_exit)
104)