#!/usr/bin/env python3
"""
exporta_exabgp.py
Lê a saída de `exabgpcli show nei sum`, interpreta o estado das sessões BGP
e envia as informações para o Zabbix via zabbix_sender, incluindo dados de descoberta (LLD).
O uptime é enviado como string legível (ex.: '1 day, 23:33:13').
 
Uso:
  python3 exporta_exabgp.py --zabbix-server 2804:54c8:cafe:85::14 --host "wanguardcliente.telic.cl"
"""
 
import subprocess
import tempfile
import argparse
import re
import shlex
import sys
import json
 
# -----------------------------
# Funções auxiliares
# -----------------------------
 
def parse_uptime_to_seconds(uptime_str):
    """Converte uptime (ex: '1 day, 22:11:12') para segundos."""
    if not uptime_str or uptime_str.strip() in ('-', 'never'):
        return 0
    uptime_str = uptime_str.strip()
    m = re.search(r'(?:(\d+)\s+day[s]?,\s*)?(\d{1,2}):(\d{2}):(\d{2})', uptime_str)
    if not m:
        m2 = re.search(r'(?:(\d+)\s+day[s]?,\s*)?(\d{1,2}):(\d{2})', uptime_str)
        if m2:
            days = int(m2.group(1)) if m2.group(1) else 0
            h = int(m2.group(2)); m_ = int(m2.group(3))
            return days*86400 + h*3600 + m_*60
        return 0
    days = int(m.group(1)) if m.group(1) else 0
    h = int(m.group(2)); mi = int(m.group(3)); s = int(m.group(4))
    return days*86400 + h*3600 + mi*60 + s
 
def run_exabgp(cmd):
    """Executa exabgpcli e retorna saída (texto)."""
    try:
        out = subprocess.check_output(shlex.split(cmd), stderr=subprocess.STDOUT, text=True)
        return out
    except subprocess.CalledProcessError as e:
        print("Erro ao executar exabgpcli:", e, file=sys.stderr)
        return ""
 
def parse_lines(output):
    """
    Processa saída do exabgpcli e retorna lista de dicionários:
    [{'peer':ip, 'asn':asn, 'uptime':str, 'uptime_sec':int, 'state':state, 'sent':int, 'recvd':int}, ...]
    """
    peers = []
    print("Saída bruta do exabgpcli:")
    print(output)
    for raw in output.splitlines():
        line = raw.strip()
        if not line or line.lower().startswith('peer') or line.lower().startswith('command'):
            continue
        print("Linha processada:", line)
        tokens = line.split()
        if len(tokens) < 6:
            continue
        peer = tokens[0]
        asn = tokens[1]
        sent = tokens[-2]
        recvd = tokens[-1]
        state = tokens[-3]
        uptime = " ".join(tokens[2:-3])
        try:
            sent_i = int(sent)
        except:
            sent_i = 0
        try:
            recvd_i = int(recvd)
        except:
            recvd_i = 0
        uptime_sec = parse_uptime_to_seconds(uptime)
        peers.append({
            'peer': peer,
            'asn': asn,
            'uptime': uptime,
            'uptime_sec': uptime_sec,
            'state': state,
            'sent': sent_i,
            'recvd': recvd_i
        })
    print("Peers processados:", peers)
    return peers
 
def build_discovery_lines(peers, zabbix_host, key_template_discovery="bgp.peer.discovery"):
    """Gera linha de descoberta (LLD) no formato JSON para o Zabbix."""
    discovery_data = {"data": [{"{#PEER}": p['peer']} for p in peers]}
    # Escapa as aspas do JSON para evitar problemas no zabbix_sender
    json_str = json.dumps(discovery_data).replace('"', '\\"')
    return [f'"{zabbix_host}" "{key_template_discovery}" "{json_str}"']
 
def build_zabbix_lines(peers, zabbix_host,
                       key_template_state="bgp.peer.state[{peer}]",
                       key_template_uptime="bgp.peer.uptime[{peer}]",
                       key_template_sent="bgp.peer.sent[{peer}]",
                       key_template_recv="bgp.peer.recvd[{peer}]"):
    """Gera linhas no formato aceito pelo zabbix_sender: <host> <key> <value>"""
    lines = []
    for p in peers:
        peer_label = p['peer']
        lines.append(f'"{zabbix_host}" "{key_template_state.format(peer=peer_label)}" "{p["state"]}"')
        lines.append(f'"{zabbix_host}" "{key_template_uptime.format(peer=peer_label)}" "{p["uptime"]}"')
        lines.append(f'"{zabbix_host}" "{key_template_sent.format(peer=peer_label)}" "{p["sent"]}"')
        lines.append(f'"{zabbix_host}" "{key_template_recv.format(peer=peer_label)}" "{p["recvd"]}"')
    return lines
 
def send_to_zabbix(lines, zabbix_server, zabbix_host, zabbix_port=None, zabbix_sender_bin='zabbix_sender'):
    """Envia as linhas para o Zabbix via zabbix_sender."""
    with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
        for l in lines:
            f.write(l + "\n")
        tmpname = f.name
 
    print(f"Conteúdo do arquivo temporário ({tmpname}):")
    with open(tmpname, 'r') as f:
        print(f.read())
 
    cmd = [zabbix_sender_bin, '-z', zabbix_server, '-s', zabbix_host, '-i', tmpname, '-vv']
    if zabbix_port:
        cmd += ['-p', str(zabbix_port)]
 
    try:
        out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
        return True, out
    except subprocess.CalledProcessError as e:
        return False, e.output
 
# -----------------------------
# Função principal
# -----------------------------
 
def main():
    parser = argparse.ArgumentParser(description="Exporta sessões BGP (exabgpcli) para Zabbix via zabbix_sender")
    parser.add_argument('--zabbix-server', required=True, help='IP/hostname do Zabbix server/proxy')
    parser.add_argument('--host', required=True, help='Nome do host como cadastrado no Zabbix')
    parser.add_argument('--zabbix-port', type=int, default=None, help='Porta do zabbix_sender (opcional)')
    parser.add_argument('--exabgp-cmd', default='exabgpcli show nei sum', help='Comando para obter peers (default: exabgpcli show nei sum)')
    parser.add_argument('--zabbix-sender-bin', default='zabbix_sender', help='Caminho para zabbix_sender')
    args = parser.parse_args()
 
    output = run_exabgp(args.exabgp_cmd)
    if not output:
        print("Nenhuma saída do exabgpcli. Abortando.", file=sys.stderr)
        sys.exit(1)
 
    peers = parse_lines(output)
    if not peers:
        print("Nenhum peer encontrado na saída.", file=sys.stderr)
        sys.exit(0)
 
    # Gera linhas de descoberta (LLD)
    discovery_lines = build_discovery_lines(peers, args.host)
    print("Linhas de descoberta geradas para o Zabbix:")
    for line in discovery_lines:
        print(line)
 
    # Gera linhas de dados
    data_lines = build_zabbix_lines(peers, args.host)
    print("Linhas de dados geradas para o Zabbix:")
    for line in data_lines:
        print(line)
 
    # Envia descoberta primeiro
    print("Enviando linhas de descoberta...")
    success_discovery, out_discovery = send_to_zabbix(discovery_lines, args.zabbix_server, args.host, args.zabbix_port, args.zabbix_sender_bin)
    if success_discovery:
        print("Descoberta enviada com sucesso para Zabbix:")
        print(out_discovery)
    else:
        print("Erro ao enviar descoberta para Zabbix:")
        print(out_discovery)
 
    # Envia dados
    print("Enviando linhas de dados...")
    success_data, out_data = send_to_zabbix(data_lines, args.zabbix_server, args.host, args.zabbix_port, args.zabbix_sender_bin)
    if success_data:
        print("Dados enviados com sucesso para Zabbix:")
        print(out_data)
    else:
        print("Erro ao enviar dados para Zabbix:")
        print(out_data)
 
    if success_discovery and success_data:
        sys.exit(0)
    else:
        sys.exit(2)
 
# -----------------------------
 
if __name__ == '__main__':
    main()