#!/usr/bin/python3
# coding=utf-8
import sys
import json

ALL_DROP_REASONS = {
    # hardware
    'sent_errs': '硬件发包出错',
    'sent_drop': '内核发包出错',
    'sent_fifo': '硬件发包出错, fifo缓冲区不足',
    'sent_carrier': '硬件发包出错, 出现冲突',
    'sent_colls': '硬件发包出错, 出现冲突',
    'recv_errs': '硬件收包出错, 可能是包解析出错',
    'recv_drop': '硬件收包出错, 可能是内存不足或错包',
    'recv_fifo': '硬件收包出错, fifo缓冲区不足, 可能是流量过大',
    'recv_frame': '硬件收包出错, 包帧错误',
    # netstat
    'TcpExt::TCPOFODrop': 'tcp协议内存不足',
    'TcpExt::TCPOFOMerge': '该报文是乱序报文，且该乱序报文携带的数据已经在乱序队列里了',
    'TcpExt::DelayedACKLost': '收到的 TCP 数据之前已经收到（可能发生虚假重传）',
    'TcpExt::TCPRcvQDrop': 'tcp协议内存不足',
    'TcpExt::TCPZeroWindowDrop': 'tcp接收窗口不足',
    'TcpExt::OfoPruned': '该报文是从tcp乱序队列中删除的',
    'TcpExt::PAWSEstabRejected': 'PAWS 检查失败',
    'TcpExt::PAWSEstab': 'PAWS 检查失败',
    'TcpExt::TCPACKSkippedSeq': 'tcp报文携带的序号不对',
    'TcpExt::TCPSYNChallenge': 'tcp处于established状态，但是收到的报文携带有syn标记',
    'TcpExt::InCsumErrors': '报文checksum值不对',
    # snmp
}

HARDWARE_DROP_KEY = ['sent_errs', 'sent_fifo', 'sent_colls',
                     'sent_carrier', 'recv_errs', 'recv_drop', 'recv_fifo', 'recv_frame']

TCP_DROP = 0
UDP_DROP = 0
ICMP_DROP = 0
HARDWARE_DROP = 0

DROP_REASONS = {}


def delta_map(pre, now):
    delta = {}
    for k, v in pre.items():
        delta[k] = now[k] - v
    return delta


def protocol_string(proto):
    global TCP_DROP
    global UDP_DROP
    global ICMP_DROP
    if proto == 1:
        ICMP_DROP += 1
        return "icmp"
    elif proto == 6:
        TCP_DROP += 1
        return "tcp"
    elif proto == 17:
        UDP_DROP += 1
        return "udp"
    else:
        return ""


def compute_hardware_drop(res):
    global HARDWARE_DROP
    devs = res[2]['dev']
    for name, value in devs.items():
        if name in res[-1]['dev']:
            value2 = res[-1]['dev'][name]
            dev = delta_map(value, value2)
            for k, v in dev.items():
                if v > 0 and k in HARDWARE_DROP_KEY:
                    HARDWARE_DROP += v


def infer_drop_reason(res):
    global ALL_DROP_REASONS
    global DROP_REASONS

    netstat = delta_map(res[0]['netstat'], res[-3]['netstat'])
    snmp = delta_map(res[1]['snmp'], res[-2]['snmp'])

    for k, v in netstat.items():
        if v > 0 and k in ALL_DROP_REASONS:
            DROP_REASONS[ALL_DROP_REASONS[k]] = 1

    for k, v in snmp.items():
        if v > 0 and k in ALL_DROP_REASONS:
            DROP_REASONS[ALL_DROP_REASONS[k]] = 1

    devs = res[2]['dev']
    for name, value in devs.items():
        if name in res[-1]['dev']:
            value2 = res[-1]['dev'][name]
            dev = delta_map(value, value2)
            for k, v in dev.items():
                if v > 0 and k in HARDWARE_DROP_KEY:
                    DROP_REASONS[ALL_DROP_REASONS[k]] = 1


def packetdrop_result(res):
    global TCP_DROP
    global UDP_DROP
    global ICMP_DROP
    global HARDWARE_DROP
    global DROP_REASONS

    postprocess_result = {
        "code": 0,
        "err_msg": "",
        "result": {},
        # "rawresult": res,
    }

    if len(res) < 6:
        postprocess_result["code"] = 1
        postprocess_result["err_msg"] = "Failed to parse output"
        print(json.dumps(postprocess_result, ensure_ascii=False))
        return

    compute_hardware_drop(res)
    packetdrop = {}
    packetdrop['packetDropList'] = {'data': []}
    droplist = []
    index = 0
    for drop in res[3: -3]:
        pstr = protocol_string(drop['proto'])
        if len(pstr) == 0:
            continue
        index += 1
        droplist.append({'key': index, 'protocol': pstr, 'local': drop['src'],
                         'remote': drop['dst'], 'dropPoint': drop['symbol']})

    packetdrop['packetDropList']['data'] = droplist

    packetdrop["packetDropSummary"] = {"data": {}}
    packetdrop["packetDropSummary"]["data"] = [
        {"key": "tcp", "value": TCP_DROP},
        {"key": "udp", "value": UDP_DROP},
        {"key": "icmp", "value": ICMP_DROP},
        {"key": "hardware", "value": HARDWARE_DROP},
    ]

    infer_drop_reason(res)
    packetdrop['packetDropAnalysis'] = {"data": {}}
    packetdrop["packetDropAnalysis"]['data'] = [
        {'key': index, 'dropReason': reason}
        for index, (reason, _) in enumerate(DROP_REASONS.items())
    ]

    postprocess_result['result'] = packetdrop
    print(json.dumps(postprocess_result, ensure_ascii=False))


def extract_params():
    path, res, task_id = sys.argv[1], [], sys.argv[2]
    with open(path, 'r') as tmp:
        for line in tmp.readlines():
            obj = json.loads(line)
            res.append(obj)

    return res, task_id


if __name__ == "__main__":
    res, _ = extract_params()
    packetdrop_result(res)
