From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: passt.top; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=D/bP4/af; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by passt.top (Postfix) with ESMTPS id 315F15A026F for ; Tue, 07 Oct 2025 22:01:18 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1759867277; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=zClj+rs1eXULybx8griXxaFxkzv3d9fNo4pQcCyctbM=; b=D/bP4/afPlwol+ZOPOqJF1s+EAGR2LDMzo+/qyuYjVb2yNI4iklDXhxv9FHvkAXgBpN7pP eHwY1y9Ni0HlPQrhLaEjgww1zZscqlhr5xslEhtsp0Do7xOBLJUo/0DEQcspzfL5DpM89n HdkZYY/A8dnzY8CsMK20J7pBObcgTEM= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-221-ApY4fGYwOw24GGOJR0ONow-1; Tue, 07 Oct 2025 16:01:15 -0400 X-MC-Unique: ApY4fGYwOw24GGOJR0ONow-1 X-Mimecast-MFC-AGG-ID: ApY4fGYwOw24GGOJR0ONow_1759867274 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-46e46486972so19820045e9.0 for ; Tue, 07 Oct 2025 13:01:14 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1759867273; x=1760472073; h=content-transfer-encoding:mime-version:organization:references :in-reply-to:message-id:subject:cc:to:from:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=zClj+rs1eXULybx8griXxaFxkzv3d9fNo4pQcCyctbM=; b=o6ijhBfVm/7wK/kDtphfekE1BOwyY517cqLQbC2lNMAZOaAXobkIJsDrBFXcvhKBoC tD6jGTi0PxM2Htpn3gJzxDGN+l7jl8TEGBgA4oQ5MW0SKuO5OSeJh/DrJo8RpZ0kcwJi bkXFUwaHxnS4dJnS3uyod9+M+aDpfNmuWaVEANjhlmfE6jjGa/otlQ5b374ON6HKlUTY xyGobWmR4aHoUhPoubU4HkK7Br+imApM7WVM8bnkVm/HZObSeOx3pYBu0WiWqwnKZ1nz Z5AlYlLn6JiUZRwvTNI19mlEy6QY7M5V6z4+hgLqm0ii+gio7jHGrx02uieaEhIlKDkf 8vVQ== X-Gm-Message-State: AOJu0Yw7VPIbZ4k6RmAhpIDKQVNzD6l9GiEIVJImgqfS9jhgEnypaMze b5Imr+RFimG1pY0Gv3We/USm0SeWODRHEfcKVKCAdIp/Is6BP8XX9cGxEhXViweNSig/YZYuW7q xItBNJEUm35nEGXmnmtffQ3Q4Jl3V+ijHGG55J7SBYRTOemyj9saZUTbBTX8SXA== X-Gm-Gg: ASbGncsTCdrQ/GBhXyhqKVy9Qrsx4GEG8dC4i7NDYpax/mlmz5mIuw2jnj7YjH8lRvo Xn3vqICkj4ekO4CJS7ooGMiOLOpgR0BkDaW6mfyRuJBqVDw0BCaHtcW15iIJUF/CKZypzhzITBc qGwwP4z5RHzg90U34BmXXw9BZai5jnXfmnc8p7zlazzpb7h+gpFob6eNr8jZjaXI4G8FH6usMMP zxbIYgj7Yie4N2ZTjt/B6k+hPrJ2RZKy+aVM3mbxX9YXHo7Qz0ua/FcKX/QoUPXAdJrdWiAu4iB JFln9obaBGAVfQ3lgekvHYc1AncY54w8WTLoSdieOhaZtJRFYbvRSLNI X-Received: by 2002:a05:600c:6383:b0:45d:d353:a491 with SMTP id 5b1f17b1804b1-46fa9a8c425mr5943795e9.1.1759867273063; Tue, 07 Oct 2025 13:01:13 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFVXNMyCIXNfYXBBPB80WmKcsYFEZsg0s2OQ1oR7EAVpxwRMZtMccFA4ShMBF6P5ItWofJbcg== X-Received: by 2002:a05:600c:6383:b0:45d:d353:a491 with SMTP id 5b1f17b1804b1-46fa9a8c425mr5943615e9.1.1759867272527; Tue, 07 Oct 2025 13:01:12 -0700 (PDT) Received: from maya.myfinge.rs (ifcgrfdd.trafficplex.cloud. [2a10:fc81:a806:d6a9::1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-46fa9bf8295sm7000925e9.3.2025.10.07.13.01.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 07 Oct 2025 13:01:11 -0700 (PDT) Date: Tue, 7 Oct 2025 22:01:10 +0200 From: Stefano Brivio To: David Gibson Subject: Re: [PATCH 3/3] test: Re-implement pasta NDP tests using tunbridge & exeter Message-ID: <20251007220110.3c8bf21c@elisabeth> In-Reply-To: <20251002075708.461931-4-david@gibson.dropbear.id.au> References: <20251002075708.461931-1-david@gibson.dropbear.id.au> <20251002075708.461931-4-david@gibson.dropbear.id.au> Organization: Red Hat X-Mailer: Claws Mail 4.2.0 (GTK 3.24.49; x86_64-pc-linux-gnu) MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: BDcdQTx3h7CvSUzxspC2PsBQ0F9lz1QhbZgzWm-P7Ac_1759867274 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Message-ID-Hash: MFVVH2GRCZO4UIRW77Q54LTYFBV3FNKI X-Message-ID-Hash: MFVVH2GRCZO4UIRW77Q54LTYFBV3FNKI X-MailFrom: sbrivio@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: passt-dev@passt.top X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: On Thu, 2 Oct 2025 17:57:08 +1000 David Gibson wrote: > Convert the pasta NDP tests from shell and our own DSL to Python using > the exeter test protocol and tunbridge network simulation library. > > Signed-off-by: David Gibson > --- > test/Makefile | 2 +- > test/pasta/dhcp | 5 ++++ > test/pasta/ndp.py | 59 ++++++++++++++++++++++++++++++++++++++++++ > test/run | 6 +++-- > test/tasst/__init__.py | 4 +++ > test/tasst/pasta.py | 40 ++++++++++++++++++++++++++++ > 6 files changed, 113 insertions(+), 3 deletions(-) > create mode 100755 test/pasta/ndp.py > create mode 100644 test/tasst/pasta.py > > diff --git a/test/Makefile b/test/Makefile > index f66c7e7e..95e3d75e 100644 > --- a/test/Makefile > +++ b/test/Makefile > @@ -67,7 +67,7 @@ ASSETS = $(DOWNLOAD_ASSETS) $(LOCAL_ASSETS) > > EXETER_SH = smoke/smoke.sh build/static_checkers.sh > EXETER_PYPATH = exeter/py3:tunbridge/:. > -EXETER_PYTHON = smoke/smoke.py build/build.py > +EXETER_PYTHON = smoke/smoke.py build/build.py pasta/ndp.py > EXETER_BATS = $(EXETER_SH:%=%.bats) $(EXETER_PYTHON:%=%.bats) > BATS_FILES = $(EXETER_BATS) \ > podman/test/system/505-networking-pasta.bats > diff --git a/test/pasta/dhcp b/test/pasta/dhcp > index e1c66be6..61279fbf 100644 > --- a/test/pasta/dhcp > +++ b/test/pasta/dhcp > @@ -18,6 +18,11 @@ test Interface name > nsout IFNAME ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname' > check [ -n "__IFNAME__" ] > > +# Bring up the interface > +ns ip link set dev __IFNAME__ up > +# Wait for SLAAC & DAD to complete > +ns while ! ip -j -6 addr show dev __IFNAME__ | jq -e '.[].addr_info.[] | select(.protocol == "kernel_ra")'; do sleep 0.1; done > + > test DHCP: address > ns /sbin/dhclient -4 --no-pid __IFNAME__ > nsout ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[0].local' > diff --git a/test/pasta/ndp.py b/test/pasta/ndp.py > new file mode 100755 > index 00000000..8c7ce31e > --- /dev/null > +++ b/test/pasta/ndp.py > @@ -0,0 +1,59 @@ > +#! /usr/bin/env python3 > +# > +# SPDX-License-Identifier: GPL-2.0-or-later > +# > +# test/pasta/ndp.py - pasta NDP functionality > +# > +# Copyright Red Hat > +# Author: David Gibson > + > +import contextlib > +import dataclasses > +from typing import Iterator > + > +import exeter > +import tunbridge > +import tasst > + > + > +@dataclasses.dataclass > +class UnconfiguredScenario(exeter.Scenario): > + """Tests for a pasta instance without --config-net""" > + > + host: tunbridge.Site > + guest: tunbridge.Site > + ifname: str > + addr6: tunbridge.ip.AddrMask6 > + gw6: tunbridge.ip.Addr6 Until this point, it looks like stuff I can happily copy and paste, and grasp, even. But then: > + @exeter.scenariotest > + def test_ifname(self) -> None: > + ifs = tunbridge.ip.ifs(self.guest) > + exeter.assert_eq(set(ifs), {'lo', self.ifname}) ...why does a "Scenario" have a .ifname? > + > + @tunbridge.ndp.NdpScenario.subscenario > + def test_ndp(self) -> tunbridge.ndp.NdpScenario: > + tunbridge.ip.ifup(self.guest, self.ifname) This raises the question of how much of tunbridge one needs to know to be able to write a basic test. Why is ifup() in 'ip'? I thought it would be more of a "link" thing. I admit I haven't had time to browse tunbridge recently, I'm just looking at this series right now. > + return tunbridge.ndp.NdpScenario(client=self.guest, > + ifname=self.ifname, > + network=self.addr6.network, > + gateway=self.gw6) This makes sense to me. > + > + > +@UnconfiguredScenario.test > +@contextlib.contextmanager > +def simh_pasta_setup() -> Iterator[UnconfiguredScenario]: > + with (tunbridge.sample.simple_host('host') as simh, > + tunbridge.sample.isolated('guest', simh.site) as guest): > + assert simh.ip6 is not None > + assert simh.gw6_ll is not None > + with tasst.pasta.pasta(simh.site, guest): > + yield UnconfiguredScenario(host=simh.site, > + guest=guest, > + ifname=simh.ifname, > + addr6=simh.ip6, > + gw6=simh.gw6_ll) ...and this too. But there's one thing I'm missing: if it's a network simulator, why do you need to call a simple_host() method to *describe* the fact that you have a host / site? That looks rather unexpected. I mean, I would have expected a syntax, in pseudocode, expressing: 1. x := node (properties such as a list of interfaces a, b, c) 2. pasta implements/connects a ...I think this is mostly embedded in the sample.simple_host() thing, but I'm not sure how. Maybe it will become clearer once I actually look into tunbridge, though. Of course, I'm trying to push away my bias coming from the fact I was, several years ago, for kselftests, aiming at something like this instead: A veth B x=$(addr A veth) B ping -c1 $x A $x vxlan B $(addr B veth) ... (where 'veth', 'vxlan' were both reserved keywords). Maybe once non-trivial links are implemented in tunbridge it will all become more obvious. > + > + > +if __name__ == '__main__': > + exeter.main() > diff --git a/test/run b/test/run > index 3872a56e..4f09d767 100755 > --- a/test/run > +++ b/test/run > @@ -43,8 +43,10 @@ KERNEL=${KERNEL:-"/boot/vmlinuz-$(uname -r)"} > > COMMIT="$(git log --oneline --no-decorate -1)" > > -# Let exeter tests written in Python find their modules > +# Let exeter tests written in Python find their modules and binaries to run > export PYTHONPATH=${BASEPATH}/exeter/py3:${BASEPATH}/tunbridge:${BASEPATH} > +export PASTA=${PASTA:-${BASEPATH}/../pasta} > + > > . lib/util > . lib/context > @@ -75,8 +77,8 @@ run() { > exeter build/build.py > exeter build/static_checkers.sh > > + exeter pasta/ndp.py > setup pasta > - test pasta/ndp > test pasta/dhcp > test pasta/tcp > test pasta/udp > diff --git a/test/tasst/__init__.py b/test/tasst/__init__.py > index fd4fe9a8..f5386b3a 100644 > --- a/test/tasst/__init__.py > +++ b/test/tasst/__init__.py > @@ -8,3 +8,7 @@ > # > # Copyright Red Hat > # Author: David Gibson > + > +from . import pasta > + > +__all__ = ['pasta'] > diff --git a/test/tasst/pasta.py b/test/tasst/pasta.py > new file mode 100644 > index 00000000..91f59036 > --- /dev/null > +++ b/test/tasst/pasta.py > @@ -0,0 +1,40 @@ > +#! /usr/bin/env python3 > +# > +# SPDX-License-Identifier: GPL-2.0-or-later > +# > +# TASST - Test A Simple Socket Transport > +# > +# test/tasst/pasta.py - Helpers for seeting up pasta instances > +# > +# Copyright Red Hat > +# Author: David Gibson > + > +import contextlib > +import os > +from typing import Iterator > + > +import tunbridge > + > + > +@contextlib.contextmanager > +def pasta(host: tunbridge.Site, guest: tunbridge.Site, *opts: str) \ > + -> Iterator[tunbridge.site.SiteProcess]: > + if tunbridge.unshare.parent(guest) is not host: > + raise ValueError("pasta guest must be a namespace under host site") > + > + # This implies guest is a namespace site > + assert isinstance(guest, tunbridge.unshare.NsenterSite) > + > + exe = os.environ['PASTA'] > + > + with host.tempdir() as piddir: > + pidfile = os.path.join(piddir, 'pasta.pid') > + cmd = [exe, '-f', '-P', pidfile] + list(opts) + [f'{guest.pid}'] > + with host.bg(*cmd, stop=True) as pasta: > + # Wait for the PID file to be written > + pidstr = None > + while not pidstr: > + pidstr = host.readfile(pidfile, check=False) > + pid = int(pidstr) > + print(f'pasta started, host: {host}, guest: {guest}, pid: {pid}') > + yield pasta ...perhaps we could also print version and path. This part also looks quite readable and intuitive to me without having looked into tunbridge recently. -- Stefano