#!/usr/bin/env python3
# VICIfast Research, Report 01: The Two Bottlenecks Inside a Busy VICIdial Server
# Test harness. FOR A TEST SERVER, NOT A LIVE FLOOR. Read it before you run it.
# Read-only AMI tally and event-ordering audit. Logs in with the AMI user bench.sh
# creates; pass its secret as the first argument.
#
# AMI tally + ordering audit. argv: secret seconds
import socket, sys, time, collections
s = socket.create_connection(('127.0.0.1', 5038)); s.settimeout(1)
s.sendall(('Action: Login\r\nUsername: vfabcount\r\nSecret: %s\r\nEvents: on\r\n\r\n' % sys.argv[1]).encode())
dur = float(sys.argv[2]); c = collections.Counter(); buf = b''; t0 = time.time()
first = {}; hung = set(); after_hangup = 0; not_first = 0
lastn = {}; reorder = 0; gaps = 0; vfload = 0; chans = set()
while time.time() - t0 < dur:
    try: d = s.recv(262144)
    except socket.timeout: continue
    if not d: break
    buf += d
    *blocks, buf = buf.split(b'\r\n\r\n')
    for b in blocks:
        f = {}
        for l in b.split(b'\r\n'):
            k, _, v = l.partition(b': ')
            if k and k not in f: f[k] = v
        ev = f.get(b'Event')
        if not ev: continue
        ev = ev.decode(errors='replace'); c[ev] += 1
        u = f.get(b'Uniqueid')
        if u:
            if ev == 'Newchannel': first.setdefault(u, 'Newchannel'); chans.add(u)
            elif u not in first: first[u] = 'pre-window'
            if u in hung and ev not in ('Hangup',): after_hangup += 1
            if ev == 'Hangup': hung.add(u)
        if ev == 'UserEvent' and f.get(b'UserEvent') == b'VFLoad':
            vfload += 1; n = int(f.get(b'N', b'-1'))
            p = lastn.get(u)
            if p is not None:
                if n <= p: reorder += 1
                elif n != p + 1: gaps += 1
            lastn[u] = n
tot = sum(c.values())
print('TOTAL %d events in %.0fs = %.0f/s | channels born in window %d | events after own Hangup %d' % (tot, dur, tot/dur, len(chans), after_hangup))
if vfload: print('VFLoad UserEvents %d (%.0f/s) on %d channels | reordered %d | gaps %d' % (vfload, vfload/dur, len(lastn), reorder, gaps))
for k, v in c.most_common(8): print('   %-22s %7d %6.1f/s' % (k, v, v/dur))
