285 lines
8.8 KiB
Bash
Executable File
285 lines
8.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
cd "$ROOT_DIR"
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage:
|
|
run_multi_rir_ccr_replay_verify.sh --rir <rir[,rir...]> [--mode snapshot|delta|both] [--keep-db]
|
|
|
|
Options:
|
|
--rir <list> Comma-separated RIR list, e.g. apnic or apnic,ripe
|
|
--mode <mode> snapshot | delta | both (default: both)
|
|
--keep-db Keep per-run RocksDB directories (default: remove after verify)
|
|
--bundle-root <p> Override bundle root
|
|
--out-root <p> Override output root (default: rpki/target/replay)
|
|
--run-tag <tag> Override timestamp suffix for all RIR runs
|
|
USAGE
|
|
}
|
|
|
|
MODE="both"
|
|
KEEP_DB=0
|
|
RIR_LIST=""
|
|
BUNDLE_ROOT="${BUNDLE_ROOT:-/home/yuyr/dev/rust_playground/routinator/bench/multi_rir_demo/runs/20260316-112341-multi-final3}"
|
|
OUT_ROOT="${OUT_ROOT:-$ROOT_DIR/target/replay}"
|
|
RUN_TAG="${RUN_TAG:-$(date -u +%Y%m%dT%H%M%SZ)}"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--rir)
|
|
shift
|
|
RIR_LIST="${1:-}"
|
|
;;
|
|
--mode)
|
|
shift
|
|
MODE="${1:-}"
|
|
;;
|
|
--keep-db)
|
|
KEEP_DB=1
|
|
;;
|
|
--bundle-root)
|
|
shift
|
|
BUNDLE_ROOT="${1:-}"
|
|
;;
|
|
--out-root)
|
|
shift
|
|
OUT_ROOT="${1:-}"
|
|
;;
|
|
--run-tag)
|
|
shift
|
|
RUN_TAG="${1:-}"
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "unknown argument: $1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
shift || true
|
|
done
|
|
|
|
if [[ -z "$RIR_LIST" ]]; then
|
|
echo "--rir is required" >&2
|
|
usage >&2
|
|
exit 2
|
|
fi
|
|
case "$MODE" in
|
|
snapshot|delta|both) ;;
|
|
*)
|
|
echo "invalid --mode: $MODE" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
CASE_INFO_SCRIPT="$ROOT_DIR/scripts/payload_replay/multi_rir_case_info.py"
|
|
mkdir -p "$OUT_ROOT"
|
|
RUN_ROOT="$OUT_ROOT/$RUN_TAG"
|
|
mkdir -p "$RUN_ROOT"
|
|
|
|
cargo build --release --bin rpki --bin ccr_to_routinator_csv --bin ccr_verify >/dev/null
|
|
|
|
summary_md="$RUN_ROOT/multi_rir_ccr_replay_verify_${RUN_TAG}_summary.md"
|
|
summary_json="$RUN_ROOT/multi_rir_ccr_replay_verify_${RUN_TAG}_summary.json"
|
|
|
|
python3 - <<'PY' >/dev/null
|
|
PY
|
|
|
|
summary_json_tmp="$(mktemp)"
|
|
printf '[]' > "$summary_json_tmp"
|
|
|
|
run_one_mode() {
|
|
local rir="$1"
|
|
local mode="$2"
|
|
local run_dir="$3"
|
|
local trust_anchor="$4"
|
|
local tal_path="$5"
|
|
local ta_path="$6"
|
|
local base_archive="$7"
|
|
local base_locks="$8"
|
|
local base_csv="$9"
|
|
local delta_archive="${10}"
|
|
local delta_locks="${11}"
|
|
local delta_csv="${12}"
|
|
local snapshot_validation_time="${13}"
|
|
local delta_validation_time="${14}"
|
|
|
|
local db_dir="$run_dir/${rir}_${mode}_db"
|
|
local report_json="$run_dir/${rir}_${mode}_report.json"
|
|
local run_log="$run_dir/${rir}_${mode}_run.log"
|
|
local ccr_path="$run_dir/${rir}_${mode}.ccr"
|
|
local csv_path="$run_dir/${rir}_${mode}_ccr_vrps.csv"
|
|
local compare_md="$run_dir/${rir}_${mode}_ccr_compare_summary.md"
|
|
local only_ours="$run_dir/${rir}_${mode}_ccr_only_in_ours.csv"
|
|
local only_record="$run_dir/${rir}_${mode}_ccr_only_in_record.csv"
|
|
local verify_json="$run_dir/${rir}_${mode}_ccr_verify.json"
|
|
local meta_json="$run_dir/${rir}_${mode}_meta.json"
|
|
|
|
rm -rf "$db_dir"
|
|
|
|
local -a cmd=(target/release/rpki --db "$db_dir" --tal-path "$tal_path" --ta-path "$ta_path")
|
|
if [[ "$mode" == "snapshot" ]]; then
|
|
cmd+=(--payload-replay-archive "$base_archive" --payload-replay-locks "$base_locks" --validation-time "$snapshot_validation_time")
|
|
else
|
|
cmd+=(
|
|
--payload-base-archive "$base_archive"
|
|
--payload-base-locks "$base_locks"
|
|
--payload-base-validation-time "$snapshot_validation_time"
|
|
--payload-delta-archive "$delta_archive"
|
|
--payload-delta-locks "$delta_locks"
|
|
--validation-time "$delta_validation_time"
|
|
)
|
|
fi
|
|
cmd+=(--report-json "$report_json" --ccr-out "$ccr_path")
|
|
|
|
local start_s end_s duration_s
|
|
start_s="$(date +%s)"
|
|
(
|
|
echo "# ${rir} ${mode} command:"
|
|
printf '%q ' "${cmd[@]}"
|
|
echo
|
|
echo
|
|
"${cmd[@]}"
|
|
) 2>&1 | tee "$run_log" >/dev/null
|
|
end_s="$(date +%s)"
|
|
duration_s="$((end_s - start_s))"
|
|
|
|
target/release/ccr_to_routinator_csv \
|
|
--ccr "$ccr_path" \
|
|
--out "$csv_path" \
|
|
--trust-anchor "$trust_anchor" >/dev/null
|
|
|
|
local record_csv
|
|
if [[ "$mode" == "snapshot" ]]; then
|
|
record_csv="$base_csv"
|
|
else
|
|
record_csv="$delta_csv"
|
|
fi
|
|
./scripts/payload_replay/compare_with_routinator_record.sh \
|
|
"$csv_path" \
|
|
"$record_csv" \
|
|
"$compare_md" \
|
|
"$only_ours" \
|
|
"$only_record" >/dev/null
|
|
|
|
target/release/ccr_verify \
|
|
--ccr "$ccr_path" \
|
|
--db "$db_dir" > "$verify_json"
|
|
|
|
python3 - "$report_json" "$meta_json" "$mode" "$duration_s" <<'PY'
|
|
import json, sys
|
|
from pathlib import Path
|
|
report = json.loads(Path(sys.argv[1]).read_text(encoding='utf-8'))
|
|
meta = {
|
|
'mode': sys.argv[3],
|
|
'duration_seconds': int(sys.argv[4]),
|
|
'validation_time': report.get('validation_time_rfc3339_utc'),
|
|
'publication_points_processed': report['tree']['instances_processed'],
|
|
'publication_points_failed': report['tree']['instances_failed'],
|
|
'vrps': len(report['vrps']),
|
|
'aspas': len(report['aspas']),
|
|
}
|
|
Path(sys.argv[2]).write_text(json.dumps(meta, ensure_ascii=False, indent=2)+'\n', encoding='utf-8')
|
|
PY
|
|
|
|
if [[ "$KEEP_DB" -eq 0 ]]; then
|
|
rm -rf "$db_dir"
|
|
fi
|
|
}
|
|
|
|
IFS=',' read -r -a RIRS <<< "$RIR_LIST"
|
|
for rir in "${RIRS[@]}"; do
|
|
rir="$(echo "$rir" | xargs)"
|
|
[[ -n "$rir" ]] || continue
|
|
eval "$(python3 "$CASE_INFO_SCRIPT" --bundle-root "$BUNDLE_ROOT" --rir "$rir" --format env)"
|
|
|
|
run_dir="$RUN_ROOT/${rir}_ccr_replay_${RUN_TAG}"
|
|
mkdir -p "$run_dir"
|
|
|
|
if [[ "$MODE" == "snapshot" || "$MODE" == "both" ]]; then
|
|
run_one_mode \
|
|
"$rir" snapshot "$run_dir" "$TRUST_ANCHOR" "$TAL_PATH" "$TA_PATH" \
|
|
"$PAYLOAD_REPLAY_ARCHIVE" "$PAYLOAD_REPLAY_LOCKS" "$ROUTINATOR_BASE_RECORD_CSV" \
|
|
"$PAYLOAD_DELTA_ARCHIVE" "$PAYLOAD_DELTA_LOCKS" "$ROUTINATOR_DELTA_RECORD_CSV" \
|
|
"$SNAPSHOT_VALIDATION_TIME" "$DELTA_VALIDATION_TIME"
|
|
fi
|
|
if [[ "$MODE" == "delta" || "$MODE" == "both" ]]; then
|
|
run_one_mode \
|
|
"$rir" delta "$run_dir" "$TRUST_ANCHOR" "$TAL_PATH" "$TA_PATH" \
|
|
"$PAYLOAD_REPLAY_ARCHIVE" "$PAYLOAD_REPLAY_LOCKS" "$ROUTINATOR_BASE_RECORD_CSV" \
|
|
"$PAYLOAD_DELTA_ARCHIVE" "$PAYLOAD_DELTA_LOCKS" "$ROUTINATOR_DELTA_RECORD_CSV" \
|
|
"$SNAPSHOT_VALIDATION_TIME" "$DELTA_VALIDATION_TIME"
|
|
fi
|
|
|
|
python3 - "$summary_json_tmp" "$run_dir" "$rir" "$MODE" <<'PY'
|
|
import json, sys
|
|
from pathlib import Path
|
|
summary_path = Path(sys.argv[1])
|
|
run_dir = Path(sys.argv[2])
|
|
rir = sys.argv[3]
|
|
mode = sys.argv[4]
|
|
rows = json.loads(summary_path.read_text(encoding='utf-8'))
|
|
for submode in ['snapshot','delta']:
|
|
if mode not in ('both', submode):
|
|
continue
|
|
compare = run_dir / f'{rir}_{submode}_ccr_compare_summary.md'
|
|
meta = run_dir / f'{rir}_{submode}_meta.json'
|
|
verify = run_dir / f'{rir}_{submode}_ccr_verify.json'
|
|
if not compare.exists() or not meta.exists() or not verify.exists():
|
|
continue
|
|
compare_text = compare.read_text(encoding='utf-8')
|
|
meta_obj = json.loads(meta.read_text(encoding='utf-8'))
|
|
verify_obj = json.loads(verify.read_text(encoding='utf-8'))
|
|
def metric(name):
|
|
prefix = f'| {name} | '
|
|
for line in compare_text.splitlines():
|
|
if line.startswith(prefix):
|
|
return int(line.split('|')[2].strip())
|
|
raise SystemExit(f'missing metric {name} in {compare}')
|
|
rows.append({
|
|
'rir': rir,
|
|
'mode': submode,
|
|
'run_dir': str(run_dir),
|
|
'duration_seconds': meta_obj['duration_seconds'],
|
|
'vrps': meta_obj['vrps'],
|
|
'aspas': meta_obj['aspas'],
|
|
'only_in_ours': metric('only_in_ours'),
|
|
'only_in_record': metric('only_in_record'),
|
|
'intersection': metric('intersection'),
|
|
'state_hashes_ok': verify_obj.get('state_hashes_ok'),
|
|
})
|
|
summary_path.write_text(json.dumps(rows, ensure_ascii=False, indent=2)+'\n', encoding='utf-8')
|
|
PY
|
|
|
|
done
|
|
|
|
python3 - "$summary_json_tmp" "$summary_json" "$summary_md" "$RUN_TAG" <<'PY'
|
|
import json, sys
|
|
from pathlib import Path
|
|
rows = json.loads(Path(sys.argv[1]).read_text(encoding='utf-8'))
|
|
out_json = Path(sys.argv[2])
|
|
out_md = Path(sys.argv[3])
|
|
run_tag = sys.argv[4]
|
|
out_json.write_text(json.dumps(rows, ensure_ascii=False, indent=2)+'\n', encoding='utf-8')
|
|
parts = []
|
|
parts.append('# Multi-RIR CCR Replay Verify Summary\n\n')
|
|
parts.append(f'- run_tag: `{run_tag}`\n\n')
|
|
parts.append('| rir | mode | duration_s | vrps | aspas | only_in_ours | only_in_record | state_hashes_ok |\n')
|
|
parts.append('|---|---|---:|---:|---:|---:|---:|---|\n')
|
|
for row in rows:
|
|
parts.append(f"| {row['rir']} | {row['mode']} | {row['duration_seconds']} | {row['vrps']} | {row['aspas']} | {row['only_in_ours']} | {row['only_in_record']} | {row['state_hashes_ok']} |\n")
|
|
out_md.write_text(''.join(parts), encoding='utf-8')
|
|
PY
|
|
|
|
rm -f "$summary_json_tmp"
|
|
|
|
echo "== multi-rir replay verify complete ==" >&2
|
|
echo "- summary: $summary_md" >&2
|
|
echo "- summary json: $summary_json" >&2
|