AxionserveSign in / RegisterEnglish/中文/cn繁体/tw

mtr-report.py

AxionServe官方ai-code2026-10-01♥ 0
🤖 AIGC disclosure: created with “AI 辅助生成 + 人工实测”, published after community-terms review.
#!/usr/bin/env python3
# mtr-report.py (FIXED) — MTR 报告解析与存证(仅标准库)
# 修复点(R7 审计 P1):
#   1) 原 NUM 正则不匹配真实 mtr 输出:跳号后是 "N.|--"(点号后无空格、带竖线),
#      原式要求 "\d+\.\s+--?" → 永远 miss;且 Loss 列带 "%",原式 ([\d.]+) 会吃掉 % 前的
#      数字后在 % 处断掉 → 整行不匹配。
#   2) 删除从未使用的 LINE 正则(死代码,且本身括号不配对)。
#   3) 兼容 "???"(超时跳)主机名与反解主机名(-z 建议照旧)。
# 用法: mtr -rwzbc 100 目标IP > raw.txt && python3 mtr-report.py raw.txt 目标IP
import sys, re, json, datetime

NUM = re.compile(
    r"^\s*(\d+)\.\s*\|?--\s*"          # 跳号 + 可选竖线 + 双横线("1.|--" / "1. --")
    r"(\S+.*?)\s+"                      # 主机(含 "???")
    r"([\d.]+)%\s+"                     # Loss%(必须带百分号)
    r"(\d+)\s+"                         # Snt
    r"([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s*$"  # Last Avg Best Wrst StDev
)

def parse(path):
    hops = []
    for ln in open(path, encoding="utf-8", errors="replace"):
        m = NUM.match(ln.rstrip())
        if not m:
            continue
        hops.append(dict(
            hop=int(m.group(1)), host=m.group(2).strip(),
            loss=float(m.group(3)), snt=int(m.group(4)),
            last=float(m.group(5)), avg=float(m.group(6)),
            best=float(m.group(7)), wrst=float(m.group(8)),
            stdev=float(m.group(9))))
    return hops

def judge(hops):
    flags = []
    last = hops[-1] if hops else None
    for h in hops:
        if h["loss"] > 0 and h is not last:
            after = [x for x in hops if x["hop"] > h["hop"]]
            if after and all(x["loss"] == 0 for x in after):
                flags.append((h["hop"], "ICMP_RATE_LIMITED",
                              "中间跳丢包且后续正常:疑似路由器限速 ICMP,非故障"))
        if h["avg"] > 0 and h["stdev"] > h["avg"] * 0.5:
            flags.append((h["hop"], "HIGH_JITTER",
                          f"StDev {h['stdev']} > Avg {h['avg']}×0.5:显著抖动"))
    verdict = "PATH_OK"
    if last and last["loss"] > 0:
        verdict = "REAL_LOSS"
        flags.append((last["hop"], "REAL_LOSS",
                      f"末跳持续丢包 {last['loss']}%:真丢包,可开工单"))
    return verdict, flags

def main():
    if len(sys.argv) < 3:
        print(__doc__); sys.exit(1)
    src, target = sys.argv[1], sys.argv[2]
    out = sys.argv[3] if len(sys.argv) > 3 else "mtr-evidence"
    hops = parse(src)
    if not hops:
        print("未解析到任何跳(确认输入是 mtr --report/-rwzbc 格式)"); sys.exit(2)
    verdict, flags = judge(hops)
    ts = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
    meta = dict(target=target, generated=ts, hops=len(hops),
                verdict=verdict, source_file=src)
    with open(f"{out}-{ts}.json", "w", encoding="utf-8") as f:
        json.dump(dict(meta=meta, hops=hops,
                       flags=[dict(hop=a, tag=b, note=c) for a, b, c in flags]),
                  f, ensure_ascii=False, indent=1)
    md = ["# MTR 存证档案", "",
          f"- 目标: `{target}`  · 生成: {ts}  · 判读: **{verdict}**", "",
          "| 跳 | 主机 | Loss% | Snt | Avg | Wrst | StDev |",
          "|---|---|---|---|---|---|---|"]
    for h in hops:
        md.append(f"| {h['hop']} | {h['host']} | {h['loss']} | {h['snt']} "
                  f"| {h['avg']} | {h['wrst']} | {h['stdev']} |")
    if flags:
        md += ["", "## 自动判读"]
        md += [f"- 第 {a} 跳 [{b}] {c}" for a, b, c in flags]
    with open(f"{out}-{ts}.md", "w", encoding="utf-8") as f:
        f.write("\n".join(md) + "\n")
    print(f"verdict={verdict} · 已写 {out}-{ts}.json / {out}-{ts}.md")

if __name__ == "__main__":
    main()

──────────────────────
【实测记录·修复版】对 mtr -rwzbc 100 样例输出解析通过(6 跳、含 ??? 超时跳、100.0% 丢包行);REAL_LOSS / ICMP_RATE_LIMITED / HIGH_JITTER 判定经构造样例验证。
【修复说明】R7 审计发现原版正则与真实 mtr 输出失配(跳号列为 "N.|--" 且 Loss 列带 %),已修正为 \d+\.\s*\|?-- 与 ([\d.]+)% 捕获,并删除未使用的 LINE 死代码。
【工单口径】把 .md 档案直接贴工单:结论行(verdict)+ 逐跳表 + 判读。
【已知边界】吃 -z(不反解)输出最稳;带反解建议 mtr --report-wide -z 重跑。
Reproduction prompt (full)
写一个 Python3 脚本 mtr-report.py:解析 mtr --report(或 mtr -rwzbc N)的文本输出(HOST: 行格式:跳号 主机 loss% snt last avg best wrst stdev),输出两份文件——JSON(结构化每跳数据+元信息)与 Markdown 存证档案(表格+自动判读结论)。判读规则:末跳 loss>0 标注 REAL_LOSS;中间跳丢包但后续跳正常标注 ICMP_RATE_LIMITED(疑似限速,非故障);stdev>avg*0.5 标注 HIGH_JITTER。命令行参数:输入文件、目标名、可选 --out 前缀。仅用标准库。
♥ 0 likesMembers: sign in to likeReport
Related workscost-calc.js (FIXED)mtr-report.pyfirst-hour-init.sh