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=fpCMa0t1; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by passt.top (Postfix) with ESMTPS id 01B1E5A0619 for ; Wed, 29 Oct 2025 00:17:34 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1761693454; 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=z8S9fqDy6YdaG5wC4sp3G9PnHCm0yyodfR/JIoB0nCM=; b=fpCMa0t1Ahw7/9viWUQRaCQpd9TZcUBJHpjYZmizpbY/smMVHlsqC3Ky4GqvgU7qi1YK4l 6Mfb0E7sppJsw5z3ENfyxckgwaIpyiPx4YgYOXzExDWefZODpN1BNEL/7as3jcr8zPhQQb QhGvrx4/KxsDgBjTaQjh7V38I6O7Fvg= Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-638-XDZd3T3YOSGLZiG4gTWSHg-1; Tue, 28 Oct 2025 19:17:32 -0400 X-MC-Unique: XDZd3T3YOSGLZiG4gTWSHg-1 X-Mimecast-MFC-AGG-ID: XDZd3T3YOSGLZiG4gTWSHg_1761693451 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-428567f67c5so3846556f8f.2 for ; Tue, 28 Oct 2025 16:17:32 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1761693450; x=1762298250; 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=sl7SLu9Y0sV7gib+woDi6uReIC6vbjbjujcS+V8T31A=; b=ItZVqOTZiySIOqFGjxrdI6BsfFk6czL1HCaPN+jbdD8uJSEQbF4jvjbCYxhnzAdA8N vkKKtQdbJXKa0C7t54WA4uzrObVCyRM/5WcRrFRhL+R8Ad/WvY+EXZScshthkEO7fDBg dcsTaDIJBfMTPJR80BcmRaHbWrq7F4OoWE19REd35NMhNsbzhQqxeWkrPfhAZuqHgt7H qrU0XLtGV9Du4RsbjnCsyaDumQef3sXPAttYusAjs1Y9LSmeEF3emCv6v48akcgMnxqY 4+fK6i170lf4fsUBDqmxO1HShuwL/Gxw/K2yC2nni8mSk8b48XWJ2AYtvAj/sOiFszzV TImg== X-Gm-Message-State: AOJu0YzQYOkh3qhWnzjNNvMgtSFHZ/uqyAKMKHAxbONjGiHySqLVFcli eAVBGOnfqHoAE/PhbEmLfSPsJX+USqZX/r3KTGMdb0se/dtnFpVcG749vNB2wm9BGHf9iBrce02 vayEt9AcjbiY7LmTDvl//S60/024wAvLm3E11kxr8xYa/pFJ1MBSgh5yqeL7UYg== X-Gm-Gg: ASbGncvcIssvzAlb4facKpwl+ZEpdr865O0CYQNQfOC0+W7EldiySkkVRTYzDp74gRf xTlHpAuss3q3AXD/XgiCreq7OAIbL+3Z3z1BwnIHV8GIotjG/GZMa57SYJvPFlzmLnqqCY7k8oE +nA5+ToSv+D4v3cdIgWvOAPIKq0LOo2fx+y6p+YKs+Mj+Yxy44yhufwa1iayJ82u4bSMBOgpHAw nLqxReS0maTd0mNKlO6W23qR4ycSaSuG7iO6EO43hiLRBWuGZX9JkKLIVfGkXAI/2U2yfkxYCIw jiddAbCBWFDzCFLubOh7dUlnh7p8Uzlyid3qlWiCQk7P5FwXOREphiPyQsNOLuez4+1hoeTkUVv NsTXd9fcojw== X-Received: by 2002:a05:6000:2088:b0:426:d5a0:bdd2 with SMTP id ffacd0b85a97d-429aed34583mr531665f8f.0.1761693449918; Tue, 28 Oct 2025 16:17:29 -0700 (PDT) X-Google-Smtp-Source: AGHT+IE/Val2LyIcmc4EFVxu9/mliCVj5Z4mUh/LRtkKxLodfj63qmhoR9k+Yw05EzT3aTCEiINgig== X-Received: by 2002:a05:6000:2088:b0:426:d5a0:bdd2 with SMTP id ffacd0b85a97d-429aed34583mr531653f8f.0.1761693449261; Tue, 28 Oct 2025 16:17:29 -0700 (PDT) Received: from maya.myfinge.rs (ifcgrfdd.trafficplex.cloud. [2a10:fc81:a806:d6a9::1]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429952df3c7sm22410899f8f.40.2025.10.28.16.17.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 28 Oct 2025 16:17:28 -0700 (PDT) Date: Wed, 29 Oct 2025 00:17:22 +0100 From: Stefano Brivio To: David Gibson Subject: Re: [PATCH 3/3] test: Re-implement pasta NDP tests using tunbridge & exeter Message-ID: <20251029001722.12e7fe07@elisabeth> In-Reply-To: References: <20251002075708.461931-1-david@gibson.dropbear.id.au> <20251002075708.461931-4-david@gibson.dropbear.id.au> <20251007220110.3c8bf21c@elisabeth> <20251009010248.1ebc1a50@elisabeth> <20251010012023.11f3a517@elisabeth> <20251016233119.4f5e155e@elisabeth> 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: k0bzgAmgy7UnxsciFOesKU3ZnfiDSOzpDp5WIQ8sIBE_1761693451 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Message-ID-Hash: QHK7DOX6V7KF23CDFCT6LUQMDIVVIGVQ X-Message-ID-Hash: QHK7DOX6V7KF23CDFCT6LUQMDIVVIGVQ 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 Fri, 17 Oct 2025 15:30:33 +1100 David Gibson wrote: > On Thu, Oct 16, 2025 at 11:31:19PM +0200, Stefano Brivio wrote: > > On Fri, 10 Oct 2025 13:17:13 +1100 > > David Gibson wrote: > > =20 > > > On Fri, Oct 10, 2025 at 01:20:23AM +0200, Stefano Brivio wrote: =20 > > > > On Thu, 9 Oct 2025 15:47:01 +1100 > > > > David Gibson wrote: > > > > =20 > > > > > On Thu, Oct 09, 2025 at 01:02:48AM +0200, Stefano Brivio wrote: = =20 > > > > > > On Wed, 8 Oct 2025 13:32:27 +1100 > > > > > > David Gibson wrote: > > > > > > =20 > > > > > > > On Tue, Oct 07, 2025 at 10:01:10PM +0200, Stefano Brivio wrot= e: =20 > > > > > > > > On Thu, 2 Oct 2025 17:57:08 +1000 > > > > > > > > David Gibson wrote: > > > > > > > > =20 > > > > > > > > > Convert the pasta NDP tests from shell and our own DSL to= Python using > > > > > > > > > the exeter test protocol and tunbridge network simulation= library. > > > > > > > > >=20 > > > > > > > > > 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 > > > > > > > > >=20 > > > > > > > > > diff --git a/test/Makefile b/test/Makefile > > > > > > > > > index f66c7e7e..95e3d75e 100644 > > > > > > > > > --- a/test/Makefile > > > > > > > > > +++ b/test/Makefile > > > > > > > > > @@ -67,7 +67,7 @@ ASSETS =3D $(DOWNLOAD_ASSETS) $(LOCAL_A= SSETS) > > > > > > > > > =20 > > > > > > > > > EXETER_SH =3D smoke/smoke.sh build/static_checkers.sh > > > > > > > > > EXETER_PYPATH =3D exeter/py3:tunbridge/:. > > > > > > > > > -EXETER_PYTHON =3D smoke/smoke.py build/build.py > > > > > > > > > +EXETER_PYTHON =3D smoke/smoke.py build/build.py pasta/nd= p.py > > > > > > > > > EXETER_BATS =3D $(EXETER_SH:%=3D%.bats) $(EXETER_PYTHON:= %=3D%.bats) > > > > > > > > > BATS_FILES =3D $(EXETER_BATS) \ > > > > > > > > > =09podman/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=09Interface name > > > > > > > > > nsout=09IFNAME ip -j link show | jq -rM '.[] | select(.l= ink_type =3D=3D "ether").ifname' > > > > > > > > > check=09[ -n "__IFNAME__" ] > > > > > > > > > =20 > > > > > > > > > +# Bring up the interface > > > > > > > > > +ns=09ip link set dev __IFNAME__ up > > > > > > > > > +# Wait for SLAAC & DAD to complete > > > > > > > > > +ns=09while ! ip -j -6 addr show dev __IFNAME__ | jq -e '= .[].addr_info.[] | select(.protocol =3D=3D "kernel_ra")'; do sleep 0.1; don= e > > > > > > > > > + > > > > > > > > > test=09DHCP: address > > > > > > > > > ns=09/sbin/dhclient -4 --no-pid __IFNAME__ > > > > > > > > > nsout=09ADDR ip -j -4 addr show|jq -rM '.[] | select(.if= name =3D=3D "__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 =20 > > > > > > > >=20 > > > > > > > > Until this point, it looks like stuff I can happily copy an= d paste, > > > > > > > > and grasp, even. But then: > > > > > > > > =20 > > > > > > > > > + @exeter.scenariotest > > > > > > > > > + def test_ifname(self) -> None: > > > > > > > > > + ifs =3D tunbridge.ip.ifs(self.guest) > > > > > > > > > + exeter.assert_eq(set(ifs), {'lo', self.ifname}) = =20 > > > > > > > >=20 > > > > > > > > ...why does a "Scenario" have a .ifname? =20 > > > > > > >=20 > > > > > > > Yeah, the readability of the Scenario mechanism was something= I was > > > > > > > particularly concerned about. I think the concept is valuabl= e, but > > > > > > > I'm very open to different ways of naming or organising it, i= f we can > > > > > > > up with something better. =20 > > > > > >=20 > > > > > > From the description you give below, the name seems to fit. > > > > > > =20 > > > > > > > A "Scenario" (specifically a subclass of exeter.Scenario) is = a group > > > > > > > of tests with a common set of parameters. In this case > > > > > > > UnconfiguredScenario is a bunch of tests about the behaviour = of pasta > > > > > > > without --config-net. Each of those tests has access to the = host and > > > > > > > guest sites, the expected interface name, address and gateway= in the > > > > > > > guest - that is, the contents of an UncofiguredScenario insta= nce. =20 > > > > > >=20 > > > > > > I'm not sure if I understand this correctly, but if each guest = has a > > > > > > single interface, that sounds a bit limiting. =20 > > > > >=20 > > > > > Sorry, to be clear: a Scenario in the general sense can contain > > > > > whatever parameters you like. This *particular* Scenario - > > > > > UnconfiguredScenario - has just those things, because those are a= ll > > > > > that its tests require. =20 > > > >=20 > > > > Ah, okay. Still, if I now want to take UnconfiguredScenario and add= a > > > > couple of dummy interfaces to it for a quick test, I guess I have t= he > > > > choice to either do that with some "external" hack, or... copy and > > > > rename it, so that it doesn't affect all the usages? =20 > > >=20 > > > No. A Scenario instance isn't responsible for managing the simulated > > > environment - that's the setup function - it's just conveying the > > > information about it that the tests need. So, you can make a setup > > > function that adds the dummy interfaces, and still yield an > > > UnconfiguredScenario. It doesn't need to have information about the > > > dummy interfaces because the tests carried by UnconfiguredScenario > > > don't care about them. =20 > >=20 > > Oh, sorry, it's a class, of course, I see now. > > =20 > > > The scenario mechanism does several things: > > > 1) Groups together some related (parameterized) tests > > > 2) Allows all of those tests to be registered at once > > > 3) Provides a mechanism for providing a bunch of information to > > > those tests (without requiring them each to have a large set of > > > direct parameters) > > >=20 > > > I'm aware that doing those things with the same construct may be > > > confusing - it's just ways of doing them separately also seem > > > confusing and/or awkward in their own ways. Maybe there's a better > > > way, but I haven't spotted it yet. =20 > >=20 > > It really is confusing to me, but the description above is rather clear > > so I'll try to propose something once I get to write some kind of setup > > function and test cases myself. =20 >=20 > Makes sense. >=20 > > > > > > Actually, I think any abstraction that doesn't offer arbitrary = sets of > > > > > > (and relationships between) the objects shown via netlink (or, = at > > > > > > least, namespaces, links, routes, addresses, neighbours) might = be > > > > > > limiting and not generic enough. =20 > > > > >=20 > > > > > Absolutely, and the abstraction does allow that. > > > > > =20 > > > > > > > That instance describes a real (simulated) environment in whi= ch we can > > > > > > > run those tests. > > > > > > >=20 > > > > > > > You use this by supplying a function which sets things up, th= en yields > > > > > > > an UnconfiguredScenario instance describing what it set up. = exeter > > > > > > > will run all of the UnconfiguredScenario tests on the environ= ment the > > > > > > > setup function created, each one as a separate test case. = =20 > > > > > >=20 > > > > > > This part is now clear to me, and I think it's not complicated = to grasp > > > > > > the concept vaguely but enough to copy, paste, and modify code = doing > > > > > > this. =20 > > > > >=20 > > > > > Ok. > > > > > =20 > > > > > > It would be even better to hide this entirely, because "yieldin= g a > > > > > > scenario" is a Python thing. In general, there's an imperative = part in > > > > > > all this (bordering functional programming, but still, not desc= riptive) > > > > > > which I struggle to see as beneficial. > > > > > >=20 > > > > > > Here the tasks at hand are, roughly: > > > > > >=20 > > > > > > 1. represent two network namespaces, with two interfaces each (= loopback > > > > > > and non-loopback), with pasta connecting one of the interfac= es of the > > > > > > inner one =20 > > > > >=20 > > > > > There's a bit more to it than that - we need to specify the host'= s > > > > > routing setup, because that will affect what pasta does. That's = what > > > > > simple_host() is about, creating a host with the single gateway > > > > > routing that's our easiest / most common case. =20 > > > >=20 > > > > Okay, sure, by "interfaces" I meant configured interfaces with > > > > addresses and a default route, too. But that doesn't really modify = my > > > > point, that is: > > > > =20 > > > > > > 2. bring up one of the interfaces > > > > > >=20 > > > > > > 3. compare addresses > > > > > >=20 > > > > > > ...and doing 1. like that is simply not... intuitive, I think. = =20 > > > > >=20 > > > > > I'm not really clear on what you're getting at here. There is an > > > > > unavoidable tradeoff here between obviousness for a single case, > > > > > versus reuseability for multiple related cases. Is it just that = some > > > > > of the relevant setup is hidden inside simple_host() that's the > > > > > problem? Or is it something else? =20 > > > >=20 > > > > ...yes, one part is that it's hidden. Another part are, specificall= y, > > > > these lines: > > > >=20 > > > > =09host: tunbridge.Site > > > > =09guest: tunbridge.Site > > > > =09ifname: str > > > >=20 > > > > =09[...] > > > >=20 > > > > =09@exeter.scenariotest > > > > =09def test_ifname(self) -> None: > > > >=20 > > > > =09[...] > > > >=20 > > > > None of these clearly links to "two network namespaces: A, with > > > > interface a1 and address x1, ...". =20 > > >=20 > > > Fair. This needs a docstring explaining the parameters / fields. =20 > >=20 > > That might help a tiny bit but I think the syntax and notations are > > kind of self-explanatory. > >=20 > > My concern is at a more conceptual level, and it's better summarised > > below, but here, specifically, we're writing: > >=20 > > host: tunbridge.Site > >=20 > > to say: > >=20 > > give me the "host" network namespace =20 >=20 > What's "me" in this sentence? The writer of the test case, that is: > > and to say that, in my ideal world, I would probably go for something > > on the line(s) of: > >=20 > > A =20 >=20 > Not really following, I hope it will make more sense to me below. if "host" is a site in "host: tunbridge.Site", which I'm calling "A" for brevity, I would like to say that with: =09A instead of: =09A: tunbridge.Site > > > > I understand this is probably very close to the bare minimum you ca= n > > > > get by modelling this all with actual code, and that's why I think > > > > actual (imperative/functional) code is usually not used to > > > > model/describe things. =20 > > >=20 > > > Imperative/functional code as opposed to..? =20 > >=20 > > ...declarative. =20 >=20 > Ok, I've generally seen functional programming put under the > declarative heading. Or as my exam invigilator famously said once > "Declaraytive Programming Paradijjums". >=20 > So, I guess that makes me unclear what does and doesn't count as > declarative for you. Hmm yes I see your point. I would have said that "purely functional programming" is declarative, and "functional programming" is a mix of declarative and imperative (in practice). In any case, I guess I later clarified this aspect. > [snip] > > > > > Syntax certainly isn't irrelevant, but so far I haven't grasped w= hat > > > > > you dislike about the function syntax versus a specialized gramme= r. > > > > > Is it: > > > > > - The irritating silly parentheses? > > > > > - Longish (qualified) function names? > > > > > - The indentation from the with syntax? > > > > > - Something else? =20 > > > >=20 > > > > It's *also* the first two =20 > > >=20 > > > Ok. First I can't easily change. Second can be mitigated by handlin= g > > > the imports differently. > > > =20 > > > > (the indentation looks actually convenient), =20 > > >=20 > > > Ok, good. I also think this is useful because it conveys the lifetim= e > > > of each object, which will be important once we get to tests where yo= u > > > need to change things part way through. > > > =20 > > > > but that's not my main point. My main point is that this isn't > > > > fundamentally declarative. You're turning it into something that > > > > resembles that, but the syntax is still from an imperative programm= ing > > > > language. > > > >=20 > > > > And in my mind the main feature of a network (topology) simulator i= s > > > > that you describe the topology (and it will build it for you), not = that > > > > you... have to build a description? > > > >=20 > > > > Using an example that's obviously familiar to you: think of taking = a > > > > device tree for some system with a couple of USB and I=C2=B2C busse= s and a > > > > flash controller, and writing all that in Python based on some form= of > > > > "bus" module/component. =20 > > >=20 > > > I mean... old school Open Firmware kind of is this, but with Forth > > > instead of Python. =20 > >=20 > > Okay, you can model data structures in Python, obviously, but that > > wasn't my point. Anyway, it's all in the example below. > > =20 > > > > Once one sees how practical device trees are for that, the Python > > > > version would look wrong, wouldn't it? =20 > > >=20 > > > That really depends on the context. If I was making an API for > > > building a device tree, I'd probably come up with something pretty > > > like this. =20 > >=20 > > ...an API for building one, yes. But not if you were writing a device > > tree itself. > > =20 > > > > Now, while I think that some single bits of DTS syntax are > > > > unnecessarily complicated, conceptually, a "networking" device tree > > > > would look more usable to me than the approach you're taking. > > > >=20 > > > > Of course, we need the whole "testing" / exeter part as well, and t= est > > > > cases are fundamentally sequential/imperative. > > > >=20 > > > > But (sorry, it's been a few years I don't touch these): > > > >=20 > > > > namespace@1 { > > > > interfaces { > > > > lo { > > > > address =3D 127.0.0.1; > > > > }; > > > > eth0 { > > > > address =3D ...; > > > > }; > > > > }; > > > > routes { > > > > /* something simpler than ip -j ro sh ? */ > > > > }; > > > > } > > > >=20 > > > > ... > > > >=20 > > > > link@... { > > > > vxlan { > > > > endpoints { > > > > a { > > > > ns =3D <&namespace@1>; > > > > }; > > > > b ... > > > >=20 > > > > ... > > > > =20 > > > > this looks much more natural to me, as an input for a simulator (I > > > > would personally make the syntax much more "elastic" by just throwi= ng a > > > > link into a namespace but I'm trying to keep it clean just for this > > > > example). =20 > > >=20 > > > Aha, I think I finally get what you're saying. More below. > > > =20 > > > > Maybe tunbridge implements this somewhere and I missed it? Or would > > > > this be part of a "Scenario" description eventually? =20 > > >=20 > > > This is entirely unrelated to what Scenario is trying to accomplish. > > > That may cause you to reconsider whether "Scenario" is a good name, > > > which is ok. > > >=20 > > >=20 > > > So. A declarative way of defining networks would be nice to have. = =20 > >=20 > > From my perspective that's fundamental, rather. I gave it for granted. > > =20 > > > I think doing it with the flexibility we want is much harder than you > > > estimate. =20 > >=20 > > I'll pretend I'm not commenting on this line by... oops. :) =20 >=20 > Heh. Well, I'd love to be proved wrong on this one. >=20 > > > It looks easy for simple static situations like the > > > examples above, but: > > >=20 > > > * If you want to describe a topology that changes partway through, > > > that's a huge step up in complexity, and kind of necessarily > > > reintroduces imperative elements. =20 > >=20 > > But you can use JSON or a ton of other formats that allow for ordering > > of elements. =20 >=20 > Just ordering network elements w.r.t. each other isn't enough. If you > have a test where you want /this/ network topology - do some stuff - > then change the toplogy to /that/ - do some more stuff. I'm not sure > how you encode that in a way that isn't imperative, or at least > pseudo-imperative. Yeah, I'll come up with a proposal, this is rather clear to me: simply interleave declarative setups with imperative test steps. > > Alternatively, one could add attributes for lifecycle and > > timing (think of nftables sets) but it looks much less intuitive than > > the alternative. =20 >=20 > I wasn't previously familiar with nftables sets. I had a quick look > at the docs, but I don't see how it relates to the current proposal. Sorry, I took this for granted: I meant timeout attributes for, say, elements in sets. That is, making the lifecycle validity an attribute of some declared object, rather than encapsulating declarative blocks in a possibly imperative sequence (if needed). > > > Device tree absolutely suffers > > > from this - that's what motivated the godawful runtime overlay > > > mechanism, and right now, I'm struggling to find time to > > > participate in the latest of many discussions about how to better > > > handle devices which can be runtime added and removed. =20 > >=20 > > I'm not suggesting that we use ANS Forth by the way. =20 >=20 > Well, we agree on that at least :). >=20 > > > * If you want to build complex scenarios out of simpler ones, you > > > need what amounts to a macro system. =20 > >=20 > > There are a ton of ways. You can also use a filesystem and includes. Or > > simply definitions of blocks, not necessarily macros, =20 >=20 > You want to parameterise the blocks - at which point it's basically > macros. >=20 > > and JSON > > implicitly gives you all that. =20 >=20 > No, it doesn't. If you have a JSON sub-object that's repeated several > times through a tree, you have to write it out, in full, each time. > YAML does allow repeating, as do some other JSON extensions, but bare > JSON does not. It still doesn't allow parameterisation of those > blocks. Until you meet RFC 6901. > > As it's nothing security relevant, I would actually go with something > > that's in theory more complicated but in practice more digestible such > > as YAML. =20 >=20 > I don't terribly like YAML, because I think there are a bunch of edge > cases where it's not obvious reading it whether something is a list or > object or somethine else. The typing and back-referencing would be > useful for this problem, I'll grant you. ...so perhaps YAML as an optional human-barely-tolerating format that we translate to JSON? Or TOML? > > > Again, a big leap up in > > > complexity. Device tree struggles with this too - it originated > > > primarily as a machine->machine format, where having heaps of > > > repeated information is fine. As it transitioned to being a > > > human->machine format, not so much. Hence /include/, expression > > > support and semi-standard running dts files through cpp before > > > compilation. It's still pretty clunky in this regard. =20 > >=20 > > It absolutely is, but that's because it was designed for a different > > purpose. > > =20 > > > Plus.. I think the interpreter for this hypothetical declarative > > > language would need an internal structure pretty similar to what > > > tunbridge, so this is kind of already a first step towards it. =20 > >=20 > > Okay, that's good to know. > >=20 > > I'm estimating I'm currently writing about 5-10 scripts per month, > > including pasta/iproute2 one-liners, setting up strange stuff, to > > reproduce / debug issues. > >=20 > > Given that this looks so fundamental for my usage I'm thinking that I > > could make at least part of this a priority of mine. > >=20 > > I realised I can implement netlink stuff and handling of networking > > configuration concepts quite fast with Rust and neli, so I'm pondering > > to write a proof of concept that can parse the example above (minus > > Forth notations, but with some kind of pointer) and make it create at > > least namespaces, links, addresses, and routes. > >=20 > > If it helps visualising how that could possibly look like with / in > > tunbridge itself, I'll take care of it soon rather than later. > >=20 > > The only little problem is that I'm much faster with Rust (because of > > neli) than I can possibly picture myself with Python, and that doesn't > > play along with tunbridge. But perhaps as a proof of concept it helps > > anyway? =20 >=20 > It would. At earlier points I did consider writing tunbridge (or > whatever I was calling the idea at the time) in Rust. In principle at > least, the lifetime tracking would make a very natural way for > ensuring you bring up / pull down things in a sensible order. >=20 > In practice, however, I almost instantly ran into intractable borrow > checker problems. Specifically, Rust's notorious difficulty with > references between parts of the same structure. That arises almost > immediately once you start building composite objects out of smaller > components: >=20 > =09struct Node { ... } > =09struct Veth<'a> { node0: &'a Node, node1: &'a Node, ... } >=20 > Then you want something that represents two nodes with a veth between > them and you get: >=20 > =09struct TwoNodes { node0: Node, node1: Node, veth: Veth<'??> } >=20 > There are ways around it, of course, but everything I found so far was > either hideously unergonomic, depended on obscure crates or both. > There are a bunch of language extensions that would help, but while > much discussed, none have landed yet. >=20 > If a way to solve this nicely appears, I'm not against moving > tunbridge to Rust. I mean, slightly more against it than I was months > ago before I made a start in Python, but still. Well, let's see how my draft turns out. I think there are obvious marketing reasons for Rust over Python, and a couple of technical ones too (speed, plus what I mentioned about neli), but there are also technical downsides as you point out. > > In general, do you think there's something in particular I could > > contribute at this stage, if I want to see my declarative dream come > > true? =20 >=20 > Yes. A bunch of pseudo-code examples - both the network declarations > and example tests that might go with them. >=20 > [snip] > > > > > > > Path I can easily add. Version would require an extra invoca= tion of > > > > > > > pasta, which I don't really want to do. =20 > > > > > >=20 > > > > > > Ah, right, never mind. The path will be good enough for that. > > > > > > =20 > > > > > > > > This part also looks > > > > > > > > quite readable and intuitive to me without having looked in= to tunbridge > > > > > > > > recently. =20 > > > > > > >=20 > > > > > > > Ok, that's promising. =20 > > > > > >=20 > > > > > > I mean, I think it's all usable for the moment, and perhaps a s= tarting > > > > > > point for some other kind of... front-end? I'm not sure. As I m= entioned > > > > > > I'm a bit worried about the potential for universal intuitivene= ss and > > > > > > usability. =20 > > > > >=20 > > > > > So am I, but I have to weigh it against being able to re-use both > > > > > tests and setups without having to re-express both in each case. = =20 > > > >=20 > > > > I think setups written like that are reusable (or can be made > > > > reusable). My usability point is about other project/usages. For pa= sst > > > > and pasta themselves, this level or reusability looks enough to me = for > > > > the foreseeable future. > > > >=20 > > > > Even though, one day, I guess we might want to generate pseudo-rand= om > > > > (fractal-tree-like?) topologies (and I was recently trying out a > > > > pasta-in-pasta-in-pasta-in-pasta-in-pasta setup to reproduce that > > > > HTTP/FIN issue). For that, a declarative approach would make things > > > > easier, I suppose. =20 > > >=20 > > > Declarative, or imperative? I actually have something like that in > > > tunbridge's selftests: a function that builds a stack of N nested > > > namespaces. > > > https://gitlab.com/dgibson/tunbridge/-/blob/main/tunbridge/unshar= e.py#L302 =20 > >=20 > > ...but they are all the same. Think, for example, of connecting every > > odd-numbered pair with veth tunnels, and every even-numbered pair with > > pasta. Say: n1 <-- veth --> n2 <-- pasta --> n3 <-- veth --> n4. =20 >=20 > That would certainly be possible. More complex, of course, but not > dramtically so. >=20 > > What's really well suited for this situation, in my experience, is a > > declarative description format that can be easily generated and > > manipulated by imperative code. =20 >=20 > Ah, so there's both an imperative and declarative component. The idea > in tunbridge is that you can do this, but rather than emit the > "declarative part" as concrete text for another parser, it's emitted > as a data structure (generally a bunch of wrappers around context > managers). >=20 > It is true that as currently designed, tunbridge builds the data > structure representation at the same time as building the actual > simulated network. With a declarative language approach, building the > description (language fragment) is separate from instantiating the > simulation. Is that something you see as valuable? Or only a side > effect of the other things about the declarative approach you like? I see that as valuable by itself, mostly because those fragments can be generated much more easily if they're separated from the imperative part. > > The name of this kind of "indirection" in computer science research > > currently escapes me, but I'm fairly sure there must be some theory > > about it. In any case, I can include something like this in my (now > > planned) proof of concept. ...still planned... --=20 Stefano