#!/usr/bin/env python3
# VICIfast Research, Report 01: The Two Bottlenecks Inside a Busy VICIdial Server
# Load rig (§7.2). FOR A THROWAWAY TEST SERVER, NOT A LIVE FLOOR. Read it before you run it.
# Once a second, does what VICIdial's AST_send_action_child.pl does: connect, read the banner, Login as the send user, one
# Ping, Logoff; logs each phase in ms. Credentials come from the servers table and are never printed.
# Changes the server? No (one AMI login a second).
# amiprobe.py: once a second, do what VICIdial's AST_send_action_child.pl does (connect, banner, Login as the
# send user, one action, Logoff) and log each phase in ms to /root/vf-lt/amiprobe.log. Credentials are read
# from /etc/astguiclient.conf and never printed.
import os, socket, time
conf = {}
for l in open('/etc/astguiclient.conf'):
    if '=>' in l:
        k, v = l.split('=>', 1); conf[k.strip()] = v.strip()
U, P = conf.get('VARasteriskUSERNAMEsend') or 'sendcron', None
# the send user's secret lives in the servers table on VICIdial; fall back to the same secret as the cron user
import subprocess
db = conf['VARDB_database']
row = subprocess.run(['mysql', db, '-N', '-B', '-e', "select ASTmgrUSERNAMEsend, ASTmgrSECRET from servers limit 1"], capture_output=True, text=True).stdout.split()
if len(row) == 2: U, P = row
out = open('/root/vf-lt/amiprobe.log', 'a', buffering=1)
def until(s, token, t_end):
    buf = b''
    while token not in buf:
        s.settimeout(max(0.01, t_end - time.time()))
        d = s.recv(65536)
        if not d: raise EOFError
        buf += d
    return buf
while not os.path.exists('/root/vf-lt/stop-sample'):
    t0 = time.time(); r = {'t': round(t0, 3)}
    try:
        s = socket.create_connection(('127.0.0.1', 5038), timeout=10); r['conn'] = round((time.time() - t0) * 1000, 1)
        until(s, b'\r\n', t0 + 10); r['banner'] = round((time.time() - t0) * 1000, 1)
        s.sendall(f'Action: Login\r\nUsername: {U}\r\nSecret: {P}\r\nEvents: off\r\n\r\n'.encode())
        until(s, b'Authentication accepted', t0 + 15); r['auth'] = round((time.time() - t0) * 1000, 1)
        s.sendall(b'Action: Ping\r\nActionID: vfp\r\n\r\n')
        until(s, b'Pong', t0 + 20); r['ping'] = round((time.time() - t0) * 1000, 1)
        s.sendall(b'Action: Logoff\r\n\r\n')
        try: until(s, b'Goodbye', t0 + 22)
        except Exception: pass
        r['done'] = round((time.time() - t0) * 1000, 1); s.close()
    except Exception as e:
        r['err'] = type(e).__name__
    out.write(' '.join(f'{k}={v}' for k, v in r.items()) + '\n')
    time.sleep(max(0, 1 - (time.time() - t0)))
