-
Notifications
You must be signed in to change notification settings - Fork 1
/
netreader.py
112 lines (102 loc) · 4.36 KB
/
netreader.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
''' Functions to ping network targets and return their status
provides:
Netreader: A class to handle SBCEye net tests
ping_target(host,timeout): test an individual target with a ping
'''
from subprocess import check_output, CalledProcessError, TimeoutExpired, PIPE
import threading
from time import sleep, time
import logging
class Netreader:
'''Read and update networ ping status
Runs a ping based network connectivity test based on the entries in a dictionary
Updates the relevant entries in data{} and logs state changes
parameters:
settings: (tuple) consisting of:
map: (dict) UI name and IP address/name
timeout: (int) Timout in seconds
data: the main data{} dictionary, a key/value pair; 'net-<name>=value'
will be added to it and the vaue updated with pin state changes.
provides:
update_pins(): processes and updates the pins
'''
def __init__(self, settings, data):
'''Setup and do initial reading'''
(self.map, self.timeout) = settings
self.states = {}
if not self.map:
print('No network addresses configured for monitoring')
return
for name,_ in self.map.items():
self.states[name] = "init"
data[f'net-{name}'] = 'U'
self.update(data)
print('Network monitoring configured and logging enabled')
logging.info('Network monitoring configured and logging enabled')
def _ping_runner(self, target, data):
'''Invokes ping_target() and waits for a return
updates the data{} dict and emits logs on status changes
- Intended to be run in parallel threads
parameters:
target: name of the remote machine to be pinged
data: the main data{} dictionary
no return
'''
address = self.map[target]
key = f'net-{target}'
(data[key], status) = ping_target(address,self.timeout)
if status:
if status != self.states[target]:
# Log new failure state
logging.info(f'Ping fail: {target} ({address}): {status}')
self.states[target] = status
else:
if self.states[target]:
# Log now responding
logging.info(f'Ping ok: {target} ({address}) in {data[key]:.1f}ms')
self.states[target] = None
def update(self, data):
'''Test each target in parallel via threads'''
threadlist = []
for target,_ in self.map.items():
gatherer = threading.Thread(target=self._ping_runner, args=[target, data])
gatherer.start()
threadlist.append(gatherer)
# Now wait till all the threads we started have terminated
start = time()
while set(threadlist) & set(threading.enumerate()):
sleep(0.1)
if time() > start + (self.timeout * 2):
threadlist = []
logging.warning('PING Lockup! this should not happen, '\
'may leave zombie threads and processes.')
def ping_target(address, timeout):
'''Returns the ping/connectivity status for a target
parameters:
address: (str) target IP/Name to ping
timeout: (int) Timeout for command in seconds
returns:
time_data: (int) response time in miliseconds or (str) 'U' if failing
err: (str) Error string for fail situations, or None
'''
time_data = 'U'
err_txt = None
try:
ping_return = check_output(['ping', '-c', '1', address],
stderr=PIPE, timeout=timeout)
except CalledProcessError as pingerror:
if pingerror.returncode == 1:
err_txt = f'Unreachable:: {pingerror.stdout.decode("utf-8").split(chr(10))[1]}'
elif pingerror.returncode == 2:
err_txt = f'Error:: {pingerror.stderr.decode("utf-8").split(chr(10))[0]}'
else:
err_txt = f'Unexpected Error ({pingerror.returncode}):: see debug'
print(f'{pingerror.stderr.decode("utf-8")}')
except TimeoutExpired:
err_txt = f'Timeout:: {timeout*1000:.0f}ms'
else:
# success, extract the time from the command return
line1 = str(ping_return.decode('utf-8').split('\n')[1])
time_string= next(x for x in line1.split(' ') if x[:5] == 'time=')
time_data = float(time_string[5:])
return time_data, err_txt