20260507_2 增加CCR/CIR差异定位链路

This commit is contained in:
2026-05-09 10:02:47 +08:00
parent 752e746b97
commit c6b408c0f9
12 changed files with 2810 additions and 247 deletions

View File

@@ -0,0 +1,188 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'USAGE'
Usage:
./scripts/periodic/compare_ccr_cir_round.sh \
--ours-ccr <path> \
--rpki-client-ccr <path> \
--out-dir <path> \
[--ours-cir <path>] \
[--rpki-client-cir <path>] \
[--trust-anchor <name>] \
[--sample-limit <n>] \
[--always-compare-cir]
USAGE
}
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
OURS_CCR=""
CLIENT_CCR=""
OURS_CIR=""
CLIENT_CIR=""
OUT_DIR=""
TRUST_ANCHOR="unknown"
SAMPLE_LIMIT="20"
ALWAYS_COMPARE_CIR=0
while [[ $# -gt 0 ]]; do
case "$1" in
--ours-ccr) OURS_CCR="$2"; shift 2 ;;
--rpki-client-ccr) CLIENT_CCR="$2"; shift 2 ;;
--ours-cir) OURS_CIR="$2"; shift 2 ;;
--rpki-client-cir) CLIENT_CIR="$2"; shift 2 ;;
--out-dir) OUT_DIR="$2"; shift 2 ;;
--trust-anchor) TRUST_ANCHOR="$2"; shift 2 ;;
--sample-limit) SAMPLE_LIMIT="$2"; shift 2 ;;
--always-compare-cir) ALWAYS_COMPARE_CIR=1; shift ;;
-h|--help) usage; exit 0 ;;
*) echo "unknown argument: $1" >&2; usage; exit 2 ;;
esac
done
[[ -n "$OURS_CCR" && -n "$CLIENT_CCR" && -n "$OUT_DIR" ]] || { usage >&2; exit 2; }
if [[ -n "$OURS_CIR" || -n "$CLIENT_CIR" ]]; then
[[ -n "$OURS_CIR" && -n "$CLIENT_CIR" ]] || { echo "--ours-cir and --rpki-client-cir must be provided together" >&2; exit 2; }
fi
mkdir -p "$OUT_DIR/ccr"
"$ROOT_DIR/scripts/periodic/compare_ccr_round.sh" \
--ours-ccr "$OURS_CCR" \
--rpki-client-ccr "$CLIENT_CCR" \
--out-dir "$OUT_DIR/ccr" \
--trust-anchor "$TRUST_ANCHOR" >/dev/null
RUN_CIR="$(python3 - <<'PY' "$OUT_DIR/ccr/compare-summary.json" "$ALWAYS_COMPARE_CIR" "$OURS_CIR" "$CLIENT_CIR"
import json
import sys
summary = json.load(open(sys.argv[1], "r", encoding="utf-8"))
always = sys.argv[2] == "1"
has_cir = bool(sys.argv[3]) and bool(sys.argv[4])
ccr_match = summary.get("stateDigestMatch") is True or summary.get("allMatch") is True
print("1" if has_cir and (always or not ccr_match) else "0")
PY
)"
if [[ "$RUN_CIR" == "1" ]]; then
mkdir -p "$OUT_DIR/cir"
"$ROOT_DIR/scripts/periodic/compare_cir_round.sh" \
--ours-cir "$OURS_CIR" \
--rpki-client-cir "$CLIENT_CIR" \
--out-dir "$OUT_DIR/cir" \
--sample-limit "$SAMPLE_LIMIT" >/dev/null
fi
python3 - <<'PY' \
"$OUT_DIR/ccr/compare-summary.json" \
"$OUT_DIR/cir/cir-compare-summary.json" \
"$OUT_DIR/summary.json" \
"$OUT_DIR/summary.md" \
"$ALWAYS_COMPARE_CIR" \
"$OURS_CIR" \
"$CLIENT_CIR" \
"$TRUST_ANCHOR" \
"$SAMPLE_LIMIT"
import json
import sys
from pathlib import Path
ccr_path = Path(sys.argv[1])
cir_path = Path(sys.argv[2])
summary_json = Path(sys.argv[3])
summary_md = Path(sys.argv[4])
always_compare_cir = sys.argv[5] == "1"
ours_cir = sys.argv[6]
client_cir = sys.argv[7]
trust_anchor = sys.argv[8]
sample_limit = int(sys.argv[9])
ccr = json.load(open(ccr_path, "r", encoding="utf-8"))
ccr_match = ccr.get("stateDigestMatch") is True or ccr.get("allMatch") is True
cir_available = bool(ours_cir) and bool(client_cir)
cir_compared = cir_path.exists()
cir = json.load(open(cir_path, "r", encoding="utf-8")) if cir_compared else None
if ccr_match:
if cir_compared and cir and cir.get("allMatch") is not True:
diagnosis = "ccr_state_same_but_cir_process_diff"
else:
diagnosis = "ccr_state_digest_match"
else:
if not cir_available:
diagnosis = "ccr_mismatch_cir_not_available"
elif not cir_compared:
diagnosis = "ccr_mismatch_cir_compare_skipped"
elif cir.get("tals", {}).get("match") is not True:
diagnosis = "tal_input_difference"
elif cir.get("objects", {}).get("match") is not True:
diagnosis = "input_object_or_manifest_accepted_set_difference"
elif (
cir.get("rejects", {}).get("match") is not True
or cir.get("rejectListSha256Match") is not True
):
diagnosis = "validation_reject_policy_difference"
else:
diagnosis = "ccr_projection_sorting_encoding_or_non_cir_state_difference"
combined = {
"allMatch": bool(ccr_match and (not cir_compared or (cir and cir.get("allMatch") is True))),
"diagnosis": diagnosis,
"comparePath": ccr.get("comparePath"),
"stateDigestMatch": ccr_match,
"mismatchedStates": ccr.get("mismatchedStates", []),
"mismatchedComponents": ccr.get("mismatchedComponents", []),
"vrps": ccr.get("vrps"),
"vaps": ccr.get("vaps"),
"trustAnchor": trust_anchor,
"sampleLimit": sample_limit,
"ccr": {
"summaryPath": str(ccr_path),
"stateDigestMatch": ccr_match,
"comparePath": ccr.get("comparePath"),
"mismatchedStates": ccr.get("mismatchedStates", []),
"mismatchedComponents": ccr.get("mismatchedComponents", []),
"vrpMatch": ccr.get("vrps", {}).get("match"),
"vapMatch": ccr.get("vaps", {}).get("match"),
},
"cir": {
"available": cir_available,
"compared": cir_compared,
"summaryPath": str(cir_path) if cir_compared else None,
"comparePolicy": "always" if always_compare_cir else "on_ccr_mismatch",
"allMatch": cir.get("allMatch") if cir else None,
"objectsMatch": cir.get("objects", {}).get("match") if cir else None,
"rejectsMatch": cir.get("rejects", {}).get("match") if cir else None,
"talsMatch": cir.get("tals", {}).get("match") if cir else None,
"rejectListSha256Match": cir.get("rejectListSha256Match") if cir else None,
"oursObjectCount": cir.get("ours", {}).get("objectCount") if cir else None,
"rpkiClientObjectCount": cir.get("rpkiClient", {}).get("objectCount") if cir else None,
"oursRejectCount": cir.get("ours", {}).get("rejectCount") if cir else None,
"rpkiClientRejectCount": cir.get("rpkiClient", {}).get("rejectCount") if cir else None,
},
}
summary_json.write_text(json.dumps(combined, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
lines = [
"# CCR/CIR Compare Summary",
"",
f"- `allMatch`: `{str(combined['allMatch']).lower()}`",
f"- `diagnosis`: `{combined['diagnosis']}`",
f"- `ccrStateDigestMatch`: `{str(ccr_match).lower()}`",
f"- `ccrMismatchedStates`: `{','.join(combined['ccr']['mismatchedStates'])}`",
f"- `cirCompared`: `{str(cir_compared).lower()}`",
]
if cir:
lines.extend([
f"- `cirObjectsMatch`: `{str(combined['cir']['objectsMatch']).lower()}`",
f"- `cirRejectsMatch`: `{str(combined['cir']['rejectsMatch']).lower()}`",
f"- `cirTalsMatch`: `{str(combined['cir']['talsMatch']).lower()}`",
f"- `cirCounts`: ours objects `{combined['cir']['oursObjectCount']}`, rpki-client objects `{combined['cir']['rpkiClientObjectCount']}`, ours rejects `{combined['cir']['oursRejectCount']}`, rpki-client rejects `{combined['cir']['rpkiClientRejectCount']}`",
])
else:
lines.append(f"- `cirSkippedReason`: `{'ccr matched' if ccr_match and not always_compare_cir else 'CIR inputs unavailable'}`")
summary_md.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(summary_json)
PY

View File

@@ -18,6 +18,7 @@ CLIENT_CCR=""
OUT_DIR=""
TRUST_ANCHOR="unknown"
CCR_TO_COMPARE_VIEWS_BIN="$ROOT_DIR/target/release/ccr_to_compare_views"
CCR_STATE_COMPARE_BIN="$ROOT_DIR/target/release/ccr_state_compare"
while [[ $# -gt 0 ]]; do
case "$1" in
@@ -34,10 +35,10 @@ done
mkdir -p "$OUT_DIR"
if [[ ! -x "$CCR_TO_COMPARE_VIEWS_BIN" ]]; then
if [[ ! -x "$CCR_STATE_COMPARE_BIN" || ! -x "$CCR_TO_COMPARE_VIEWS_BIN" ]]; then
(
cd "$ROOT_DIR"
cargo build --release --bin ccr_to_compare_views
cargo build --release --bin ccr_state_compare --bin ccr_to_compare_views
)
fi
@@ -48,76 +49,13 @@ CLIENT_VAPS="$OUT_DIR/rpki-client-vaps.csv"
SUMMARY_JSON="$OUT_DIR/compare-summary.json"
SUMMARY_MD="$OUT_DIR/compare-summary.md"
"$CCR_TO_COMPARE_VIEWS_BIN" \
--ccr "$OURS_CCR" \
--vrps-out "$OURS_VRPS" \
--vaps-out "$OURS_VAPS" \
--trust-anchor "$TRUST_ANCHOR"
"$CCR_TO_COMPARE_VIEWS_BIN" \
--ccr "$CLIENT_CCR" \
--vrps-out "$CLIENT_VRPS" \
--vaps-out "$CLIENT_VAPS" \
--trust-anchor "$TRUST_ANCHOR"
python3 - <<'PY' "$OURS_VRPS" "$CLIENT_VRPS" "$OURS_VAPS" "$CLIENT_VAPS" "$SUMMARY_JSON" "$SUMMARY_MD"
import csv
import json
import sys
from pathlib import Path
ours_vrps_path, client_vrps_path, ours_vaps_path, client_vaps_path, json_out, md_out = sys.argv[1:]
def rows(path):
with open(path, newline="") as f:
return list(csv.reader(f))[1:]
ours_vrps = {tuple(r) for r in rows(ours_vrps_path)}
client_vrps = {tuple(r) for r in rows(client_vrps_path)}
ours_vaps = {tuple(r) for r in rows(ours_vaps_path)}
client_vaps = {tuple(r) for r in rows(client_vaps_path)}
summary = {
"vrps": {
"ours": len(ours_vrps),
"rpkiClient": len(client_vrps),
"match": ours_vrps == client_vrps,
"onlyInOurs": sorted(ours_vrps - client_vrps)[:20],
"onlyInRpkiClient": sorted(client_vrps - ours_vrps)[:20],
},
"vaps": {
"ours": len(ours_vaps),
"rpkiClient": len(client_vaps),
"match": ours_vaps == client_vaps,
"onlyInOurs": sorted(ours_vaps - client_vaps)[:20],
"onlyInRpkiClient": sorted(client_vaps - ours_vaps)[:20],
},
}
summary["allMatch"] = summary["vrps"]["match"] and summary["vaps"]["match"]
Path(json_out).write_text(json.dumps(summary, indent=2), encoding="utf-8")
lines = [
"# Round Compare Summary",
"",
f"- `allMatch`: `{summary['allMatch']}`",
f"- `vrpMatch`: `{summary['vrps']['match']}`",
f"- `vapMatch`: `{summary['vaps']['match']}`",
f"- `ours_vrps`: `{summary['vrps']['ours']}`",
f"- `rpki_client_vrps`: `{summary['vrps']['rpkiClient']}`",
f"- `ours_vaps`: `{summary['vaps']['ours']}`",
f"- `rpki_client_vaps`: `{summary['vaps']['rpkiClient']}`",
"",
"## Sample Differences",
"",
f"- `vrps.onlyInOurs`: `{len(summary['vrps']['onlyInOurs'])}`",
f"- `vrps.onlyInRpkiClient`: `{len(summary['vrps']['onlyInRpkiClient'])}`",
f"- `vaps.onlyInOurs`: `{len(summary['vaps']['onlyInOurs'])}`",
f"- `vaps.onlyInRpkiClient`: `{len(summary['vaps']['onlyInRpkiClient'])}`",
]
Path(md_out).write_text("\n".join(lines) + "\n", encoding="utf-8")
PY
"$CCR_STATE_COMPARE_BIN" \
--ours-ccr "$OURS_CCR" \
--rpki-client-ccr "$CLIENT_CCR" \
--out-json "$SUMMARY_JSON" \
--out-md "$SUMMARY_MD" \
--out-dir "$OUT_DIR" \
--trust-anchor "$TRUST_ANCHOR" \
--fallback-compare-views
echo "$OUT_DIR"

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'USAGE'
Usage:
./scripts/periodic/compare_cir_round.sh \
--ours-cir <path> \
--rpki-client-cir <path> \
--out-dir <path> \
[--sample-limit <n>]
USAGE
}
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
OURS_CIR=""
CLIENT_CIR=""
OUT_DIR=""
SAMPLE_LIMIT="20"
CIR_STATE_COMPARE_BIN="$ROOT_DIR/target/release/cir_state_compare"
while [[ $# -gt 0 ]]; do
case "$1" in
--ours-cir) OURS_CIR="$2"; shift 2 ;;
--rpki-client-cir) CLIENT_CIR="$2"; shift 2 ;;
--out-dir) OUT_DIR="$2"; shift 2 ;;
--sample-limit) SAMPLE_LIMIT="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "unknown argument: $1" >&2; usage; exit 2 ;;
esac
done
[[ -n "$OURS_CIR" && -n "$CLIENT_CIR" && -n "$OUT_DIR" ]] || { usage >&2; exit 2; }
mkdir -p "$OUT_DIR"
if [[ ! -x "$CIR_STATE_COMPARE_BIN" ]]; then
(cd "$ROOT_DIR" && cargo build --release --bin cir_state_compare)
fi
"$CIR_STATE_COMPARE_BIN" \
--ours-cir "$OURS_CIR" \
--rpki-client-cir "$CLIENT_CIR" \
--out-json "$OUT_DIR/cir-compare-summary.json" \
--out-md "$OUT_DIR/cir-compare-summary.md" \
--sample-limit "$SAMPLE_LIMIT"
echo "$OUT_DIR"