update
This commit is contained in:
Executable
+306
@@ -0,0 +1,306 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
script=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)/B32-qemu-run.sh
|
||||
|
||||
if test "${1:-}" != --worker; then
|
||||
test "$#" -eq 1 || {
|
||||
printf '%s\n' 'usage: B32-qemu-run.sh TC168-cache-inflight|TC184-vnode-races' >&2
|
||||
exit 20
|
||||
}
|
||||
case "$1" in
|
||||
TC168-cache-inflight|TC184-vnode-races) ;;
|
||||
*) printf 'unknown B32 QEMU scenario: %s\n' "$1" >&2; exit 20 ;;
|
||||
esac
|
||||
total=${PRE15_QEMU_TIMEOUT:-1200}
|
||||
case "$total" in
|
||||
''|*[!0-9]*|0) printf '%s\n' 'invalid PRE15_QEMU_TIMEOUT' >&2; exit 20 ;;
|
||||
esac
|
||||
exec timeout -k 30 "$total" "$script" --worker "$1"
|
||||
fi
|
||||
|
||||
shift
|
||||
test "$#" -eq 1 || exit 20
|
||||
scenario=$1
|
||||
: "${PRE15_EVIDENCE_ROOT:?PRE15_EVIDENCE_ROOT is required}"
|
||||
: "${PRE15_QEMU_BASE_IMAGE:?PRE15_QEMU_BASE_IMAGE is required}"
|
||||
: "${PRE15_QEMU_SSH_KEY:?PRE15_QEMU_SSH_KEY is required}"
|
||||
|
||||
dut=$(CDPATH= cd -- "$(dirname -- "$script")/../../.." && pwd -P)
|
||||
root=$(CDPATH= cd -- "$dut/.." && pwd -P)
|
||||
lib=$dut/tests/pre15/lib
|
||||
case_script=$dut/tests/pre15/cases/B32-cache-state.sh
|
||||
freebsd_src=${PRE15_FREEBSD_SRC:-${FREEBSD_SRC:-/work/build/freebsd-src}}
|
||||
user=${PRE15_QEMU_SSH_USER:-root}
|
||||
boot_timeout=${PRE15_QEMU_BOOT_TIMEOUT:-180}
|
||||
guest_timeout=${PRE15_GUEST_COMMAND_TIMEOUT:-60}
|
||||
case_timeout=${PRE15_B32_QEMU_CASE_TIMEOUT:-900}
|
||||
run_id=$(date -u '+%Y%m%dT%H%M%SZ')-qemu-B32-$scenario-$$
|
||||
run=$PRE15_EVIDENCE_ROOT/$run_id
|
||||
case_run=$run/case
|
||||
case_tmp=$run/case-tmp
|
||||
overlay=$run/guest-overlay.qcow2
|
||||
control=$run/ssh-control
|
||||
serial=$run/serial.log
|
||||
qemu_log=$run/qemu.log
|
||||
ownership=$case_run/ownership.tsv
|
||||
cleanup_log=$case_run/cleanup.log
|
||||
target_marker=$case_run/target.marker
|
||||
reason_file=$case_run/reason.txt
|
||||
fixture_hashes=$case_run/fixture-hashes.tsv
|
||||
module_hash=$case_run/module-hash.tsv
|
||||
qemu_pid=
|
||||
port=
|
||||
ssh_ready=0
|
||||
cleanup_done=0
|
||||
cleanup_status=PASS
|
||||
|
||||
mkdir -p "$case_run" "$case_tmp"
|
||||
: >"$ownership"
|
||||
: >"$fixture_hashes"
|
||||
: >"$module_hash"
|
||||
: >"$reason_file"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
test "$cleanup_done" -eq 0 || return
|
||||
cleanup_done=1
|
||||
if test "$ssh_ready" -eq 1; then
|
||||
PRE15_QEMU_SSH_PORT=$port
|
||||
PRE15_QEMU_SSH_USER=$user
|
||||
PRE15_QEMU_SSH_KEY=$PRE15_QEMU_SSH_KEY
|
||||
PRE15_QEMU_CONTROL_PATH=$control
|
||||
PRE15_GUEST_COMMAND_TIMEOUT=$guest_timeout
|
||||
PRE15_OWNERSHIP_FILE=$ownership
|
||||
PRE15_CLEANUP_LOG=$cleanup_log
|
||||
PRE15_RUN_DIR=$case_run
|
||||
export PRE15_QEMU_SSH_PORT PRE15_QEMU_SSH_USER PRE15_QEMU_SSH_KEY
|
||||
export PRE15_QEMU_CONTROL_PATH PRE15_GUEST_COMMAND_TIMEOUT
|
||||
export PRE15_OWNERSHIP_FILE PRE15_CLEANUP_LOG PRE15_RUN_DIR
|
||||
. "$lib/runner.sh"
|
||||
pre15_cleanup_owned || cleanup_status=FAIL
|
||||
test "${PRE15_CLEANUP_STATUS_RESULT:-FAIL}" = PASS || cleanup_status=FAIL
|
||||
else
|
||||
printf '%s\n' 'PASS no guest resources reached before SSH readiness' \
|
||||
>"$cleanup_log"
|
||||
fi
|
||||
if test -S "$control"; then
|
||||
timeout -k 5 15 ssh -S "$control" -O exit -p "${port:-22}" \
|
||||
"$user@127.0.0.1" >>"$run/host-cleanup.log" 2>&1 || true
|
||||
fi
|
||||
if test -n "$qemu_pid" && test "$qemu_pid" != 26318 && \
|
||||
kill -0 "$qemu_pid" 2>/dev/null; then
|
||||
kill -TERM "$qemu_pid" 2>/dev/null || cleanup_status=FAIL
|
||||
wait_count=0
|
||||
while kill -0 "$qemu_pid" 2>/dev/null && test "$wait_count" -lt 100; do
|
||||
sleep 0.1
|
||||
wait_count=$((wait_count + 1))
|
||||
done
|
||||
if kill -0 "$qemu_pid" 2>/dev/null; then
|
||||
kill -KILL "$qemu_pid" 2>/dev/null || cleanup_status=FAIL
|
||||
sleep 0.2
|
||||
fi
|
||||
fi
|
||||
if test -n "$qemu_pid" && kill -0 "$qemu_pid" 2>/dev/null; then
|
||||
cleanup_status=FAIL
|
||||
printf 'FAIL owned QEMU PID survived: %s\n' "$qemu_pid" \
|
||||
>>"$run/host-cleanup.log"
|
||||
else
|
||||
printf 'PASS owned QEMU PID stopped: %s\n' "${qemu_pid:-not-started}" \
|
||||
>>"$run/host-cleanup.log"
|
||||
fi
|
||||
if test -f "$overlay"; then
|
||||
rm -f -- "$overlay" || cleanup_status=FAIL
|
||||
fi
|
||||
test ! -e "$overlay" || cleanup_status=FAIL
|
||||
printf 'overlay_removed=%s\n' "$(test ! -e "$overlay" && printf yes || printf no)" \
|
||||
>>"$run/host-cleanup.log"
|
||||
rm -rf -- "$case_tmp"
|
||||
stat -c 'path=%n size=%s mode=%a inode=%i mtime=%Y ctime=%Z' \
|
||||
"$PRE15_QEMU_BASE_IMAGE" >"$run/base-identity-after.txt" || \
|
||||
cleanup_status=FAIL
|
||||
if ! cmp -s "$run/base-identity-before.txt" "$run/base-identity-after.txt"; then
|
||||
cleanup_status=FAIL
|
||||
printf '%s\n' 'FAIL protected base metadata changed' \
|
||||
>>"$run/host-cleanup.log"
|
||||
else
|
||||
printf '%s\n' 'PASS protected base metadata unchanged' \
|
||||
>>"$run/host-cleanup.log"
|
||||
fi
|
||||
if test -n "$port"; then
|
||||
if timeout 10s python3 -B - "$port" <<'PY'
|
||||
import socket
|
||||
import sys
|
||||
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.settimeout(0.2)
|
||||
raise SystemExit(0 if sock.connect_ex(("127.0.0.1", int(sys.argv[1]))) != 0 else 1)
|
||||
PY
|
||||
then
|
||||
printf 'PASS dynamic port free: %s\n' "$port" \
|
||||
>>"$run/host-cleanup.log"
|
||||
else
|
||||
cleanup_status=FAIL
|
||||
printf 'FAIL dynamic port remains open: %s\n' "$port" \
|
||||
>>"$run/host-cleanup.log"
|
||||
fi
|
||||
fi
|
||||
printf '%s\n' "$cleanup_status" >"$run/cleanup.status"
|
||||
}
|
||||
|
||||
finish()
|
||||
{
|
||||
rc=$?
|
||||
trap - EXIT HUP INT TERM
|
||||
cleanup
|
||||
if test "$cleanup_status" != PASS; then
|
||||
printf '%s\n' 'RUNNER_FAIL owned cleanup or base metadata check failed' \
|
||||
>>"$run/RESULT.txt"
|
||||
rc=20
|
||||
fi
|
||||
exit "$rc"
|
||||
}
|
||||
|
||||
trap finish EXIT HUP INT TERM
|
||||
|
||||
for tool in cmp qemu-img qemu-system-x86_64 ssh stat timeout; do
|
||||
command -v "$tool" >/dev/null 2>&1 || {
|
||||
printf 'INFRA_BLOCKED missing QEMU tool: %s\n' "$tool" >"$run/RESULT.txt"
|
||||
exit 21
|
||||
}
|
||||
done
|
||||
test -f "$PRE15_QEMU_BASE_IMAGE" || {
|
||||
printf '%s\n' 'INFRA_BLOCKED protected base image is absent' >"$run/RESULT.txt"
|
||||
exit 21
|
||||
}
|
||||
test -f "$PRE15_QEMU_SSH_KEY" || {
|
||||
printf '%s\n' 'INFRA_BLOCKED SSH key is absent' >"$run/RESULT.txt"
|
||||
exit 21
|
||||
}
|
||||
|
||||
stat -c 'path=%n size=%s mode=%a inode=%i mtime=%Y ctime=%Z' \
|
||||
"$PRE15_QEMU_BASE_IMAGE" >"$run/base-identity-before.txt"
|
||||
timeout -k 5 30 qemu-img info --output=json "$PRE15_QEMU_BASE_IMAGE" \
|
||||
>"$run/base-qemu-img-info.json"
|
||||
timeout -k 5 30 qemu-img create -f qcow2 -F qcow2 \
|
||||
-b "$PRE15_QEMU_BASE_IMAGE" "$overlay" >"$run/qemu-img-create.stdout" \
|
||||
2>"$run/qemu-img-create.stderr"
|
||||
|
||||
port=$(timeout 10s python3 -B - <<'PY'
|
||||
import socket
|
||||
|
||||
while True:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.bind(("127.0.0.1", 0))
|
||||
port = sock.getsockname()[1]
|
||||
if port != 9222:
|
||||
print(port)
|
||||
break
|
||||
PY
|
||||
)
|
||||
python3 -B - "$run/qemu-argv.json" "$overlay" "$port" "$serial" <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
argv = [
|
||||
"qemu-system-x86_64", "-accel", "tcg,thread=multi", "-cpu", "qemu64",
|
||||
"-m", "6144", "-smp", "4",
|
||||
"-drive", f"file={sys.argv[2]},if=virtio,format=qcow2",
|
||||
"-netdev", f"user,id=net0,hostfwd=tcp:127.0.0.1:{sys.argv[3]}-:22",
|
||||
"-device", "virtio-net-pci,netdev=net0", "-display", "none",
|
||||
"-serial", f"file:{sys.argv[4]}", "-monitor", "none",
|
||||
]
|
||||
Path(sys.argv[1]).write_text(json.dumps(argv, separators=(",", ":")) + "\n", encoding="ascii")
|
||||
PY
|
||||
|
||||
qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64 -m 6144 -smp 4 \
|
||||
-drive "file=$overlay,if=virtio,format=qcow2" \
|
||||
-netdev "user,id=net0,hostfwd=tcp:127.0.0.1:$port-:22" \
|
||||
-device virtio-net-pci,netdev=net0 -display none -serial "file:$serial" \
|
||||
-monitor none >"$run/qemu.stdout" 2>"$qemu_log" &
|
||||
qemu_pid=$!
|
||||
printf '%s\n' "$qemu_pid" >"$run/qemu.pid"
|
||||
test "$qemu_pid" != 26318 || {
|
||||
printf '%s\n' 'RUNNER_FAIL QEMU PID collided with protected PID' >"$run/RESULT.txt"
|
||||
exit 20
|
||||
}
|
||||
|
||||
boot_marker=$run/ssh-ready
|
||||
boot_start=$(date -u '+%s')
|
||||
set +e
|
||||
timeout -s KILL "$boot_timeout" env B32_QEMU_PID="$qemu_pid" \
|
||||
B32_QEMU_PORT="$port" B32_QEMU_USER="$user" \
|
||||
B32_QEMU_KEY="$PRE15_QEMU_SSH_KEY" B32_QEMU_MARKER="$boot_marker" \
|
||||
/bin/sh -c '
|
||||
while kill -0 "$B32_QEMU_PID" 2>/dev/null; do
|
||||
if timeout -s KILL 8 ssh -o BatchMode=yes \
|
||||
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
-o ConnectTimeout=5 -i "$B32_QEMU_KEY" \
|
||||
-p "$B32_QEMU_PORT" "[email protected]" true \
|
||||
>/dev/null 2>&1; then
|
||||
: >"$B32_QEMU_MARKER"
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
exit 21
|
||||
'
|
||||
boot_rc=$?
|
||||
set -e
|
||||
boot_end=$(date -u '+%s')
|
||||
printf 'start_epoch=%s\nend_epoch=%s\nelapsed_seconds=%s\ndeadline_seconds=%s\nexit=%s\n' \
|
||||
"$boot_start" "$boot_end" "$((boot_end - boot_start))" "$boot_timeout" \
|
||||
"$boot_rc" >"$run/boot-timing.txt"
|
||||
if test -f "$boot_marker"; then
|
||||
ssh_ready=1
|
||||
elif ! kill -0 "$qemu_pid" 2>/dev/null; then
|
||||
printf '%s\n' 'INFRA_BLOCKED QEMU exited before guest SSH' >"$run/RESULT.txt"
|
||||
exit 21
|
||||
else
|
||||
printf 'INFRA_BLOCKED guest SSH not ready within absolute %ss boot deadline\n' \
|
||||
"$boot_timeout" >"$run/RESULT.txt"
|
||||
exit 21
|
||||
fi
|
||||
|
||||
if ! timeout -k 5 30 ssh -MNf -o BatchMode=yes -o StrictHostKeyChecking=no \
|
||||
-o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -o ControlMaster=yes \
|
||||
-o "ControlPath=$control" -i "$PRE15_QEMU_SSH_KEY" -p "$port" \
|
||||
"$user@127.0.0.1"; then
|
||||
printf '%s\n' 'INFRA_BLOCKED SSH ControlMaster failed' >"$run/RESULT.txt"
|
||||
exit 21
|
||||
fi
|
||||
|
||||
export PRE15_MODE=qemu
|
||||
export PRE15_DUT=$dut PRE15_ROOT=$root PRE15_LIB_DIR=$lib
|
||||
export PRE15_CASE_TMP=$case_tmp PRE15_RUN_DIR=$case_run
|
||||
export PRE15_FREEBSD_SRC=$freebsd_src PRE15_B32_SCENARIO=$scenario
|
||||
export PRE15_QEMU_CONTROL_PATH=$control PRE15_QEMU_SSH_PORT=$port
|
||||
export PRE15_QEMU_SSH_USER=$user PRE15_QEMU_SSH_KEY
|
||||
export PRE15_GUEST_COMMAND_TIMEOUT=$guest_timeout
|
||||
export PRE15_OWNERSHIP_FILE=$ownership PRE15_CLEANUP_LOG=$cleanup_log
|
||||
export PRE15_TARGET_MARKER_FILE=$target_marker PRE15_REASON_FILE=$reason_file
|
||||
export PRE15_FIXTURE_HASHES=$fixture_hashes PRE15_MODULE_HASH=$module_hash
|
||||
|
||||
set +e
|
||||
timeout -k 10 "$case_timeout" /bin/sh "$case_script" \
|
||||
>"$case_run/stdout.log" 2>"$case_run/stderr.log"
|
||||
case_rc=$?
|
||||
set -e
|
||||
case "$case_rc" in
|
||||
0)
|
||||
if test -f "$target_marker"; then
|
||||
printf '%s PASS target=reached\n' "$scenario" >"$run/RESULT.txt"
|
||||
else
|
||||
printf '%s\n' 'RUNNER_FAIL case exited zero without target marker' \
|
||||
>"$run/RESULT.txt"
|
||||
case_rc=20
|
||||
fi
|
||||
;;
|
||||
10) printf '%s DUT_FAIL\n' "$scenario" >"$run/RESULT.txt" ;;
|
||||
20) printf '%s RUNNER_FAIL\n' "$scenario" >"$run/RESULT.txt" ;;
|
||||
21) printf '%s INFRA_BLOCKED\n' "$scenario" >"$run/RESULT.txt" ;;
|
||||
124|137) printf '%s RUNNER_FAIL case-timeout=%ss\n' "$scenario" "$case_timeout" >"$run/RESULT.txt"; case_rc=20 ;;
|
||||
*) printf '%s RUNNER_FAIL exit=%s\n' "$scenario" "$case_rc" >"$run/RESULT.txt"; case_rc=20 ;;
|
||||
esac
|
||||
exit "$case_rc"
|
||||
Reference in New Issue
Block a user