Convert the old-style tests for pasta (DHCP, NDP, TCP and UDP transfers) to using avocado. There are a few differences in what we test, but this should generally improve coverage: * We run in a constructed network environment, so we no longer depend on the real host's networking configuration * We do independent setup for each individual test * We add explicit tests for --config-net, which we use to accelerate that setup for the TCP and UDP tests * The TCP and UDP tests now test transfers between the guest and a (simulated) remote site that's on a different network from the simulated pasta host. Thus testing the no NAT case that passt/pasta emphasizes. (We need to add tests for the NAT cases back in). Signed-off-by: David Gibson <david(a)gibson.dropbear.id.au> --- test/Makefile | 4 +- test/pasta/.gitignore | 1 + test/pasta/pasta.py | 138 +++++++++++++++++++++++++++++++++++++++++ test/tasst/__main__.py | 2 +- test/tasst/pasta.py | 52 ++++++++++++++++ 5 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 test/pasta/.gitignore create mode 100644 test/pasta/pasta.py create mode 100644 test/tasst/pasta.py diff --git a/test/Makefile b/test/Makefile index 6748d38a..ba249a5d 100644 --- a/test/Makefile +++ b/test/Makefile @@ -64,11 +64,11 @@ LOCAL_ASSETS = mbuto.img mbuto.mem.img podman/bin/podman QEMU_EFI.fd \ $(TESTDATA_ASSETS) ASSETS = $(DOWNLOAD_ASSETS) $(LOCAL_ASSETS) -AVOCADO_ASSETS = +AVOCADO_ASSETS = nstool small.bin medium.bin big.bin META_ASSETS = nstool small.bin medium.bin big.bin EXETER_SH = build/static_checkers.sh -EXETER_PY = build/build.py +EXETER_PY = build/build.py pasta/pasta.py EXETER_JOBS = $(EXETER_SH:%.sh=%.json) $(EXETER_PY:%.py=%.json) AVOCADO_JOBS = $(EXETER_JOBS) avocado/static_checkers.json diff --git a/test/pasta/.gitignore b/test/pasta/.gitignore new file mode 100644 index 00000000..a6c57f5f --- /dev/null +++ b/test/pasta/.gitignore @@ -0,0 +1 @@ +*.json diff --git a/test/pasta/pasta.py b/test/pasta/pasta.py new file mode 100644 index 00000000..491927a6 --- /dev/null +++ b/test/pasta/pasta.py @@ -0,0 +1,138 @@ +#! /usr/bin/env avocado-runner-avocado-classless + +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright Red Hat +# Author: David Gibson <david(a)gibson.dropbear.id.au> + +""" +avocado/pasta.py - Basic tests for pasta mode +""" + +import contextlib +import ipaddress + +import exeter + +from tasst import dhcp, dhcpv6, ndp, nstool +from tasst.pasta import Pasta +from tasst.scenario.simple import simple_net + +IN_FWD_PORT = 10002 +SPLICE_FWD_PORT = 10003 +FWD_OPTS = ['-t', f'{IN_FWD_PORT}', '-u', f'{IN_FWD_PORT}', + '-T', f'{SPLICE_FWD_PORT}', '-U', f'{SPLICE_FWD_PORT}'] + + +(a)contextlib.contextmanager +def pasta_unconfigured(*opts): + with simple_net() as simnet: + with nstool.unshare_snh('pastans', '-Ucnpf', '--mount-proc', + parent=simnet.simhost, capable=True) \ + as guestns: + with Pasta(host=simnet.simhost, opts=opts, ns=guestns) as pasta: + yield simnet, pasta.ns + + +(a)exeter.test +def test_ifname(): + with pasta_unconfigured() as (simnet, ns): + expected = set(['lo', simnet.IFNAME]) + exeter.assert_eq(set(ns.ifs()), expected) + + +(a)contextlib.contextmanager +def pasta_ndp_setup(): + with pasta_unconfigured() as (simnet, guestns): + guestns.ifup(simnet.IFNAME) + yield ndp.NdpTestScenario(client=guestns, + ifname=simnet.IFNAME, + network=simnet.IP6.network, + gateway=simnet.gw_ip6_ll.ip) + + +ndp.ndp_tests(pasta_ndp_setup) + + +(a)contextlib.contextmanager +def pasta_dhcp(): + with pasta_unconfigured() as (simnet, guestns): + yield dhcp.DhcpTestScenario(client=guestns, + ifname=simnet.IFNAME, + addr=simnet.IP4.ip, + gateway=simnet.GW_IP4.ip, + mtu=65520) + + +dhcp.dhcp_tests(pasta_dhcp) + + +(a)contextlib.contextmanager +def pasta_dhcpv6(): + with pasta_unconfigured() as (simnet, guestns): + yield dhcpv6.Dhcpv6TestScenario(client=guestns, + ifname=simnet.IFNAME, + addr=simnet.IP6.ip) + + +dhcpv6.dhcp6_tests(pasta_dhcpv6) + + +(a)contextlib.contextmanager +def pasta_configured(): + with pasta_unconfigured('--config-net', *FWD_OPTS) as (simnet, ns): + # Wait for DAD to complete on the --config-net address + ns.addr_wait(simnet.IFNAME, family='inet6', scope='global') + yield simnet, ns + + +(a)exeter.test +def test_config_net_addr(): + with pasta_configured() as (simnet, ns): + addrs = ns.addrs(simnet.IFNAME, scope='global') + exeter.assert_eq(set(addrs), set([simnet.IP4, simnet.IP6])) + + +(a)exeter.test +def test_config_net_route4(): + with pasta_configured() as (simnet, ns): + (defroute,) = ns.routes4(dst='default') + gateway = ipaddress.ip_address(defroute['gateway']) + exeter.assert_eq(gateway, simnet.GW_IP4.ip) + + +(a)exeter.test +def test_config_net_route6(): + with pasta_configured() as (simnet, ns): + (defroute,) = ns.routes6(dst='default') + gateway = ipaddress.ip_address(defroute['gateway']) + exeter.assert_eq(gateway, simnet.gw_ip6_ll.ip) + + +(a)exeter.test +def test_config_net_mtu(): + with pasta_configured() as (simnet, ns): + mtu = ns.mtu(simnet.IFNAME) + exeter.assert_eq(mtu, 65520) + + +(a)contextlib.contextmanager +def outward_transfer(): + with pasta_configured() as (simnet, ns): + yield ns, simnet.gw + + +(a)contextlib.contextmanager +def inward_transfer(): + with pasta_configured() as (simnet, ns): + yield simnet.gw, ns + + +(a)contextlib.contextmanager +def spliced_transfer(): + with pasta_configured() as (simnet, ns): + yield ns, simnet.simhost + + +if __name__ == '__main__': + exeter.main() diff --git a/test/tasst/__main__.py b/test/tasst/__main__.py index 491c68c9..058b3746 100644 --- a/test/tasst/__main__.py +++ b/test/tasst/__main__.py @@ -13,7 +13,7 @@ library of test helpers for passt & pasta import exeter # We import just to get the exeter tests, which flake8 can't see -from . import dhcp, dhcpv6, ndp, nstool, snh, transfer # noqa: F401 +from . import dhcp, dhcpv6, ndp, nstool, pasta, snh, transfer # noqa: F401 from .scenario import simple # noqa: F401 from .selftest import static_ifup, veth # noqa: F401 diff --git a/test/tasst/pasta.py b/test/tasst/pasta.py new file mode 100644 index 00000000..030affce --- /dev/null +++ b/test/tasst/pasta.py @@ -0,0 +1,52 @@ +#! /usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright Red Hat +# Author: David Gibson <david(a)gibson.dropbear.id.au> + +""" +Test A Simple Socket Transport + +pasta.py - Helpers for starting pasta +""" + +import contextlib +import os.path +import tempfile + + +PASTA_BIN = './pasta' + + +class Pasta(contextlib.AbstractContextManager): + """A managed pasta instance""" + + def __init__(self, *, host, ns, opts): + self.host = host + self.ns = ns + self.opts = opts + self.proc = None + + def __enter__(self): + self.tmpdir = tempfile.TemporaryDirectory() + piddir = self.tmpdir.__enter__() + pidfile = os.path.join(piddir, 'pasta.pid') + relpid = self.ns.relative_pid(self.host) + cmd = [f'{PASTA_BIN}', '-f', '-P', f'{pidfile}'] + list(self.opts) + \ + [f'{relpid}'] + self.proc = self.host.bg(*cmd) + self.proc.__enter__() + # Wait for the PID file to be written + pidstr = None + while not pidstr: + pidstr = self.host.output('cat', f'{pidfile}', check=False) + self.pid = int(pidstr) + return self + + def __exit__(self, *exc_details): + try: + self.host.fg('kill', '-TERM', f'{self.pid}') + self.proc.__exit__(*exc_details) + finally: + self.tmpdir.__exit__(*exc_details) -- 2.45.2