test: Improve robustness of pinging from within namespaces

It turns out that the semantics of ping's -c option does not match our
expectations. The manual says:

> Stop after sending count ECHO_REQUEST packets. With deadline option,
> ping waits for count ECHO_REPLY packets, until the timeout expires.

But in fact, something like an ICMP_DEST_UNREACH will also count as a
response, meaning that the process will terminate with non-zero
exitcode even though the deadline has not yet been crossed, rather
than keep waiting for an ECHO_REPLY.

To add some extra flavor: this is only the case for iputils' ping,
Busybox's implementation will hold out for a response until the
deadline expires.

Therefore, wrap the ping in an ugly retry loop.

Also, return the full available context from must{,_not}_reach on
failures, instead of just the output. This allows us to show the exit
code in the top-level exception handler.
This commit is contained in:
Tobias Waldekranz
2024-05-17 23:14:42 +02:00
parent 605d28a0da
commit 9777e3fccf
+11 -8
View File
@@ -136,15 +136,18 @@ class IsolatedMacVlan:
result.append(l)
return result
def ping(self, daddr, count=1, timeout=5, interval=2, check=False):
return self.runsh(f"""set -ex; ping -c {count} -w {timeout} -i {interval} {daddr}""", check=check)
def ping(self, daddr, timeout=5):
return self.run(["timeout", str(timeout), "/bin/sh"], text=True, check=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
input=f"while :; do ping -c1 -w1 {daddr} && break; done")
def must_reach(self, *args, **kwargs):
res = self.ping(*args, **kwargs)
if res.returncode != 0:
raise Exception(res.stdout)
self.ping(*args, **kwargs)
def must_not_reach(self, *args, **kwargs):
res = self.ping(*args, **kwargs)
if res.returncode == 0:
raise Exception(res.stdout)
try:
res = self.ping(*args, **kwargs)
except subprocess.CalledProcessError as e:
return
raise Exception(res)