Files
lmxopcua/scripts/validation/scadabridge-sdk-compatibility/rig-failover.sh
T
Joseph Doherty 438484fdca
v2-ci / unit-tests (push) Has been cancelled
test(validation): ScadaBridge consumer matrix — failover suite, UNS subscribe, ConditionRefresh after reconnect (ScadaBridge#125)
Probe 'failover <primary> <backup> <mode> <dir>' drives ScadaBridge's own DataConnectionActor
(production primary/backup machine, real DataConnectionFactory, default DataConnectionOptions,
FailoverRetryCount 3) against two servers; run.py loses the primary (SIGKILL or SIGSTOP) once the
probe reports a steady primary, and the probe requires Bad quality on loss, "Connected to backup",
the backup's own value, a fresh ConditionRefresh replaying the native condition, and a write via the
backup. rig-failover.sh runs the same probe against the local docker-dev central pair.

Matrix legs gain a UNS subscription case and a Server-object alarm subscription on the reconnected
session whose ConditionRefresh must replay the active condition (11 cases). run.py gains --versions,
--suites, --failover-modes and --failover-loss.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
2026-11-12 09:28:01 -05:00

47 lines
2.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# ScadaBridge#125 — the failover probe against the REAL docker-dev OtOpcUa pair (central-1 :4840 /
# central-2 :4841), not the in-process fixture. ScadaBridge's DataConnectionActor connects to central-1
# as primary and central-2 as backup; once it reports a steady primary, central-1's container is killed
# (docker kill = abrupt loss) and the probe must land on central-2 with Good values and a fresh
# ConditionRefresh. central-1 is started again afterwards. LOCAL docker-dev only — never a wonder box.
#
# Usage: rig-failover.sh <Probe.dll> <new-result-directory>
# Anonymous (OpcUa:AllowAnonymous=true on the rig) cannot write there, so writes are off; the rig deploys
# no active native alarm, so the alarm check is "ConditionRefresh completes on each node" only.
set -euo pipefail
probe=$1; out=$2
primary_container=${RIG_PRIMARY_CONTAINER:-otopcua-dev-central-1-1}
backup_container=${RIG_BACKUP_CONTAINER:-otopcua-dev-central-2-1}
mkdir -p "$out"
for c in "$primary_container" "$backup_container"; do
mkdir -p "$out/server-certs/$c"
docker cp "$c:/app/pki/own/certs/." "$out/server-certs/$c/"
done
export SCADABRIDGE_PROBE_ANONYMOUS=1
export SCADABRIDGE_PROBE_SERVER_CERTS="$out/server-certs/$primary_container:$out/server-certs/$backup_container"
export SCADABRIDGE_PROBE_FAILOVER_TAGS=${RIG_TAGS:-"nsu=https://zb.com/otopcua/raw;s=int3b-mb-01/plc/HR100,nsu=https://zb.com/otopcua/uns;s=a/l/e1/HR5"}
export SCADABRIDGE_PROBE_FAILOVER_WRITE_TAG=none
export SCADABRIDGE_PROBE_FAILOVER_BACKUP_VALUE=none
export SCADABRIDGE_PROBE_FAILOVER_ALARM_PREFIX=none
dotnet "$probe" failover opc.tcp://localhost:${RIG_PRIMARY_PORT:-4840} opc.tcp://localhost:${RIG_BACKUP_PORT:-4841} None "$out/consumer" \
> "$out/consumer.log" 2>&1 &
pid=$!
for _ in $(seq 1 1500); do
[[ -f "$out/consumer/primary-ready" ]] && break
kill -0 "$pid" 2>/dev/null || break
sleep 0.1
done
status=0
if [[ -f "$out/consumer/primary-ready" ]]; then
python3 -c 'from datetime import datetime, timezone; print(datetime.now(timezone.utc).isoformat())' > "$out/consumer/primary-killed.tmp"
docker kill "$primary_container" > /dev/null
mv "$out/consumer/primary-killed.tmp" "$out/consumer/primary-killed"
wait "$pid" || status=$?
docker start "$primary_container" > /dev/null
else
wait "$pid" || status=$?
fi
docker inspect "$primary_container" "$backup_container" --format '{{.Name}} {{.Config.Image}} {{.Image}} {{.State.Status}}' > "$out/containers.txt"
echo "probe exit $status; result $out/consumer/result.json"
exit "$status"