LookingGlas/modules/mtr_parser.py

94 lines
No EOL
2.8 KiB
Python

import statistics
class MtrHop:
def __init__(self, idx):
self.idx = idx
self.host = '???'
self.timings = []
self.received_count = 0
class MtrParser:
def __init__(self):
self.hops = {}
def update(self, line):
parts = line.strip().split()
if len(parts) < 3:
return
type, idx_str, value = parts[0], parts[1], parts[2]
if type not in ('h', 'd', 'p'):
return
try:
idx = int(idx_str)
except ValueError:
return
if idx not in self.hops:
self.hops[idx] = MtrHop(idx)
hop = self.hops[idx]
if type == 'h':
hop.host = value
elif type == 'p':
hop.received_count += 1
try:
hop.timings.append(float(value) / 1000.0)
except ValueError:
pass
def render_table(self):
if not self.hops:
return ""
sorted_hops = sorted(self.hops.values(), key=lambda h: h.idx)
if len(sorted_hops) >= 2 and sorted_hops[-1].host == sorted_hops[-2].host:
sorted_hops.pop(-1)
col_widths = {'host': 43, 'loss': 7, 'snt': 5, 'last': 7, 'avg': 7, 'best': 7, 'wrst': 7, 'stdev': 7}
header = (
f"{'Host':<{col_widths['host']}}"
f"{'Loss%':>{col_widths['loss']}}"
f"{'Snt':>{col_widths['snt']}}"
f"{'Last':>{col_widths['last']}}"
f"{'Avg':>{col_widths['avg']}}"
f"{'Best':>{col_widths['best']}}"
f"{'Wrst':>{col_widths['wrst']}}"
f"{'StDev':>{col_widths['stdev']}}\n"
)
output = header
for hop in sorted_hops:
received = hop.received_count
loss_percent = 0.0
if received > 0:
last, avg, best, worst, stdev = (
hop.timings[-1],
statistics.mean(hop.timings),
min(hop.timings),
max(hop.timings),
statistics.stdev(hop.timings) if received > 1 else 0.0,
)
else:
last, avg, best, worst, stdev = 0.0, 0.0, 0.0, 0.0, 0.0
host_str = f" {hop.idx:d}.|-- {hop.host}"
loss_str = f"{loss_percent:.1f}%"
row = (
f"{host_str:<{col_widths['host']}}"
f"{loss_str:>{col_widths['loss']}}"
f"{hop.received_count:>{col_widths['snt']}}"
f"{last:>{col_widths['last']}.1f}"
f"{avg:>{col_widths['avg']}.1f}"
f"{best:>{col_widths['best']}.1f}"
f"{worst:>{col_widths['wrst']}.1f}"
f"{stdev:>{col_widths['stdev']}.1f}\n"
)
output += row
return output