#!/usr/bin/env bash set -euo pipefail usage() { cat <<'EOF' Usage: ./scripts/cir/run_cir_replay_ours.sh \ --cir \ --repo-bytes-db \ --out-dir \ --reference-ccr \ [--keep-db] \ [--write-actual-ccr] \ [--write-report-json] \ [--report-json-compact] \ [--phase2-object-workers ] \ [--phase2-worker-queue-capacity ] \ [--rpki-bin ] \ [--real-rsync-bin ] EOF } ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" CIR="" REPO_BYTES_DB="" OUT_DIR="" REFERENCE_CCR="" KEEP_DB=0 WRITE_ACTUAL_CCR=0 WRITE_REPORT_JSON=0 REPORT_JSON_COMPACT=0 PHASE2_OBJECT_WORKERS="${CIR_REPLAY_PHASE2_OBJECT_WORKERS:-4}" PHASE2_WORKER_QUEUE_CAPACITY="${CIR_REPLAY_PHASE2_WORKER_QUEUE_CAPACITY:-64}" RPKI_BIN="${RPKI_BIN:-$ROOT_DIR/target/release/rpki}" CIR_MATERIALIZE_BIN="${CIR_MATERIALIZE_BIN:-$ROOT_DIR/target/release/cir_materialize}" CIR_EXTRACT_INPUTS_BIN="${CIR_EXTRACT_INPUTS_BIN:-$ROOT_DIR/target/release/cir_extract_inputs}" CCR_TO_COMPARE_VIEWS_BIN="${CCR_TO_COMPARE_VIEWS_BIN:-$ROOT_DIR/target/release/ccr_to_compare_views}" REAL_RSYNC_BIN="${REAL_RSYNC_BIN:-/usr/bin/rsync}" WRAPPER="$ROOT_DIR/scripts/cir/cir-rsync-wrapper" while [[ $# -gt 0 ]]; do case "$1" in --cir) CIR="$2"; shift 2 ;; --repo-bytes-db) REPO_BYTES_DB="$2"; shift 2 ;; --out-dir) OUT_DIR="$2"; shift 2 ;; --reference-ccr) REFERENCE_CCR="$2"; shift 2 ;; --keep-db) KEEP_DB=1; shift ;; --write-actual-ccr) WRITE_ACTUAL_CCR=1; shift ;; --write-report-json) WRITE_REPORT_JSON=1; shift ;; --report-json-compact) WRITE_REPORT_JSON=1; REPORT_JSON_COMPACT=1; shift ;; --phase2-object-workers) PHASE2_OBJECT_WORKERS="$2"; shift 2 ;; --phase2-worker-queue-capacity) PHASE2_WORKER_QUEUE_CAPACITY="$2"; shift 2 ;; --rpki-bin) RPKI_BIN="$2"; shift 2 ;; --real-rsync-bin) REAL_RSYNC_BIN="$2"; shift 2 ;; -h|--help) usage; exit 0 ;; *) echo "unknown argument: $1" >&2; usage; exit 2 ;; esac done [[ -n "$CIR" && -n "$REPO_BYTES_DB" && -n "$OUT_DIR" && -n "$REFERENCE_CCR" ]] || { usage >&2 exit 2 } mkdir -p "$OUT_DIR" needs_build=0 if [[ ! -x "$RPKI_BIN" || ! -x "$CIR_MATERIALIZE_BIN" || ! -x "$CIR_EXTRACT_INPUTS_BIN" || ! -x "$CCR_TO_COMPARE_VIEWS_BIN" ]]; then needs_build=1 elif [[ "$RPKI_BIN" == "$ROOT_DIR/target/release/rpki" ]] && find "$ROOT_DIR/src" "$ROOT_DIR/Cargo.toml" -newer "$RPKI_BIN" -print -quit | grep -q .; then needs_build=1 fi if [[ "$needs_build" -eq 1 ]]; then ( cd "$ROOT_DIR" cargo build --release --bin rpki --bin cir_materialize --bin cir_extract_inputs --bin ccr_to_compare_views ) fi TMP_ROOT="$OUT_DIR/.tmp" TALS_DIR="$TMP_ROOT/tals" META_JSON="$TMP_ROOT/meta.json" MIRROR_ROOT="$TMP_ROOT/mirror" DB_DIR="$TMP_ROOT/work-db" REPLAY_RAW_STORE_DB="$TMP_ROOT/replay-raw-store.db" REPLAY_REPO_BYTES_DB="$TMP_ROOT/replay-repo-bytes.db" ACTUAL_CCR="$OUT_DIR/actual.ccr" ACTUAL_REPORT="$OUT_DIR/report.json" ACTUAL_VRPS="$OUT_DIR/actual-vrps.csv" ACTUAL_VAPS="$OUT_DIR/actual-vaps.csv" REF_VRPS="$OUT_DIR/reference-vrps.csv" REF_VAPS="$OUT_DIR/reference-vaps.csv" COMPARE_JSON="$OUT_DIR/compare-summary.json" RUN_LOG="$OUT_DIR/run.log" rm -rf "$TMP_ROOT" mkdir -p "$TMP_ROOT" "$CIR_EXTRACT_INPUTS_BIN" --cir "$CIR" --tals-dir "$TALS_DIR" --meta-json "$META_JSON" materialize_cmd=("$CIR_MATERIALIZE_BIN" --cir "$CIR" --repo-bytes-db "$REPO_BYTES_DB" --mirror-root "$MIRROR_ROOT") if [[ "$KEEP_DB" -eq 1 ]]; then materialize_cmd+=(--keep-db) fi "${materialize_cmd[@]}" VALIDATION_TIME="$(python3 - <<'PY' "$META_JSON" import json,sys print(json.load(open(sys.argv[1]))["validationTime"]) PY )" mapfile -t TAL_PATHS < <(python3 - <<'PY' "$META_JSON" import json, sys for item in json.load(open(sys.argv[1], encoding="utf-8"))["talFiles"]: print(item["path"]) PY ) TAL_ARGS=() for tal_path in "${TAL_PATHS[@]}"; do TAL_ARGS+=(--tal-path "$tal_path") done export CIR_MIRROR_ROOT="$(python3 - <<'PY' "$MIRROR_ROOT" from pathlib import Path import sys print(Path(sys.argv[1]).resolve()) PY )" export REAL_RSYNC_BIN="$REAL_RSYNC_BIN" export CIR_LOCAL_LINK_MODE=1 REPORT_JSON_ARGS=(--skip-report-build) VCIR_ARGS=(--skip-vcir-persist) if [[ "$WRITE_REPORT_JSON" -eq 1 ]]; then REPORT_JSON_ARGS=(--report-json "$ACTUAL_REPORT") if [[ "$REPORT_JSON_COMPACT" -eq 1 ]]; then REPORT_JSON_ARGS+=(--report-json-compact) fi fi CCR_ARGS=() if [[ "$WRITE_ACTUAL_CCR" -eq 1 ]]; then CCR_ARGS=(--ccr-out "$ACTUAL_CCR") fi "$RPKI_BIN" \ --db "$DB_DIR" \ --raw-store-db "$REPLAY_RAW_STORE_DB" \ --repo-bytes-db "$REPLAY_REPO_BYTES_DB" \ "${TAL_ARGS[@]}" \ --parallel-phase2-object-workers "$PHASE2_OBJECT_WORKERS" \ --parallel-phase2-worker-queue-capacity "$PHASE2_WORKER_QUEUE_CAPACITY" \ --disable-rrdp \ --rsync-command "$WRAPPER" \ --validation-time "$VALIDATION_TIME" \ "${CCR_ARGS[@]}" \ --vrps-csv-out "$ACTUAL_VRPS" \ --vaps-csv-out "$ACTUAL_VAPS" \ --compare-view-trust-anchor unknown \ "${VCIR_ARGS[@]}" \ "${REPORT_JSON_ARGS[@]}" \ >"$RUN_LOG" 2>&1 sort_compare_csv() { local path="$1" local tmp="${path}.sorted.tmp" { head -n 1 "$path" tail -n +2 "$path" | LC_ALL=C sort -u } >"$tmp" mv "$tmp" "$path" } sort_compare_csv "$ACTUAL_VRPS" sort_compare_csv "$ACTUAL_VAPS" "$CCR_TO_COMPARE_VIEWS_BIN" --ccr "$REFERENCE_CCR" --vrps-out "$REF_VRPS" --vaps-out "$REF_VAPS" --trust-anchor unknown python3 - <<'PY' "$ACTUAL_VRPS" "$REF_VRPS" "$ACTUAL_VAPS" "$REF_VAPS" "$COMPARE_JSON" "$META_JSON" "$WRITE_REPORT_JSON" "$WRITE_ACTUAL_CCR" "$PHASE2_OBJECT_WORKERS" "$PHASE2_WORKER_QUEUE_CAPACITY" import csv, json, sys def next_row(reader): try: return tuple(next(reader)) except StopIteration: return None def compare_sorted_csv(actual_path, ref_path): actual_count = 0 ref_count = 0 only_actual_count = 0 only_ref_count = 0 only_actual_sample = [] only_ref_sample = [] with open(actual_path, newline="") as actual_file, open(ref_path, newline="") as ref_file: actual_reader = csv.reader(actual_file) ref_reader = csv.reader(ref_file) next(actual_reader, None) next(ref_reader, None) actual = next_row(actual_reader) ref = next_row(ref_reader) while actual is not None or ref is not None: if ref is None or (actual is not None and actual < ref): actual_count += 1 only_actual_count += 1 if len(only_actual_sample) < 20: only_actual_sample.append(list(actual)) actual = next_row(actual_reader) elif actual is None or ref < actual: ref_count += 1 only_ref_count += 1 if len(only_ref_sample) < 20: only_ref_sample.append(list(ref)) ref = next_row(ref_reader) else: actual_count += 1 ref_count += 1 actual = next_row(actual_reader) ref = next_row(ref_reader) return { "actual": actual_count, "reference": ref_count, "only_in_actual": only_actual_sample, "only_in_reference": only_ref_sample, "only_in_actual_count": only_actual_count, "only_in_reference_count": only_ref_count, "match": only_actual_count == 0 and only_ref_count == 0, } vrps = compare_sorted_csv(sys.argv[1], sys.argv[2]) vaps = compare_sorted_csv(sys.argv[3], sys.argv[4]) meta = json.load(open(sys.argv[6], encoding="utf-8")) summary = { "compareMode": "trust-anchor-agnostic", "talCount": len(meta["talFiles"]), "talPaths": [item["path"] for item in meta["talFiles"]], "actualCcrWritten": sys.argv[8] == "1", "reportJsonWritten": sys.argv[7] == "1", "replayParallelism": { "phase2ObjectWorkers": int(sys.argv[9]), "phase2WorkerQueueCapacity": int(sys.argv[10]), }, "vrps": vrps, "vaps": vaps, } with open(sys.argv[5], "w") as f: json.dump(summary, f, indent=2) PY if [[ "$KEEP_DB" -ne 1 ]]; then rm -rf "$TMP_ROOT" fi echo "done: $OUT_DIR"