[Test] functional: wait for rspamd ports to free in teardown
Under pabot each worker runs many suites sequentially on the SAME port
range (base + worker_index*100). Rspamd Teardown did Terminate Process +
Wait For Process, but that only reaps the MAIN rspamd; the listening
sockets are shared with forked workers and can linger a beat after main
exits. The next suite's rspamd on that worker then races them and dies:
rspamd_inet_address_listen: bind 127.0.0.1:57090 failed: 98,
'Address already in use'
spawn_workers: cannot listen on normal socket 127.0.0.1:57090
Process Is Gone (rc=1, port=57089)
which cascades the whole shared-rspamd suite (e.g. 001_merged -> 250+
failures) or single suites like 440_ssl_server. rspamd sets SO_REUSEADDR
before bind, so this is NOT TIME_WAIT -- it is a still-LISTENing socket
from a not-yet-fully-gone worker.
Add port_is_free() (rspamd.py) and a Wait For Rspamd Ports Released
keyword, called from Rspamd Teardown after Wait For Process: block (up to
~6s, warn-not-fail) until the normal + controller ports actually refuse
connections before releasing the suite. Closes the handoff race window.
This is a pre-existing flake (same bind-98 signature on master, e.g.
fedora job for #6067 with :56990), independent of the dummy-port
templating in this branch; both CI runs of this PR hit it in different
suites, the tell-tale of nondeterministic infra flake.
Verified: the keyword runs on every teardown (357 invocations / 714 port
checks in a 4-worker pabot run) and port_is_free correctly passes on a
free port and blocks on a live listener; no regression in serial or
parallel runs. The race itself is timing-dependent and reproduces under
CI container contention rather than locally, so CI is the real check.