From: Stefano Brivio <sbrivio@redhat.com>
To: David Gibson <david@gibson.dropbear.id.au>
Cc: passt-dev@passt.top
Subject: Re: [PATCH 3/3] test: Re-implement pasta NDP tests using tunbridge & exeter
Date: Tue, 7 Oct 2025 22:01:10 +0200 [thread overview]
Message-ID: <20251007220110.3c8bf21c@elisabeth> (raw)
In-Reply-To: <20251002075708.461931-4-david@gibson.dropbear.id.au>
On Thu, 2 Oct 2025 17:57:08 +1000
David Gibson <david@gibson.dropbear.id.au> 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 <david@gibson.dropbear.id.au>
> ---
> 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 <david@gibson.dropbear.id.au>
> +
> +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 <david@gibson.dropbear.id.au>
> +
> +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 <david@gibson.dropbear.id.au>
> +
> +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
next prev parent reply other threads:[~2025-10-07 20:01 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-02 7:57 [PATCH 0/3] RFC: Preview of tunbridge based tests David Gibson
2025-10-02 7:57 ` [PATCH 1/3] test: Prepare for " David Gibson
2025-10-07 20:00 ` Stefano Brivio
2025-10-02 7:57 ` [PATCH 2/3] test: Add some missing quoting in exeter runner David Gibson
2025-10-02 7:57 ` [PATCH 3/3] test: Re-implement pasta NDP tests using tunbridge & exeter David Gibson
2025-10-07 20:01 ` Stefano Brivio [this message]
2025-10-02 7:57 ` [PATCH 0/3] RFC: Preview of tunbridge based tests David Gibson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251007220110.3c8bf21c@elisabeth \
--to=sbrivio@redhat.com \
--cc=david@gibson.dropbear.id.au \
--cc=passt-dev@passt.top \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://passt.top/passt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).