aboutgitcodebugslistschat
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/.gitignore2
-rw-r--r--test/Makefile93
-rw-r--r--test/README.md35
-rw-r--r--test/build/all61
-rwxr-xr-xtest/build/build.py110
-rw-r--r--test/build/clang_tidy17
-rw-r--r--test/build/cppcheck17
-rwxr-xr-xtest/build/static_checkers.sh42
-rw-r--r--test/demo/podman4
-rw-r--r--test/lib/exeter66
-rwxr-xr-xtest/lib/setup4
-rwxr-xr-xtest/lib/term33
-rwxr-xr-xtest/lib/test7
-rw-r--r--test/memory/passt2
-rw-r--r--test/migrate/basic4
-rw-r--r--test/migrate/basic_fin4
-rw-r--r--test/migrate/iperf3_bidir64
-rw-r--r--test/migrate/iperf3_in64
-rw-r--r--test/migrate/iperf3_many_out64
-rw-r--r--test/migrate/iperf3_out64
-rw-r--r--test/migrate/rampstream_in4
-rw-r--r--test/migrate/rampstream_out4
-rwxr-xr-xtest/passt.mbuto11
-rwxr-xr-xtest/passt.mem.mbuto8
-rw-r--r--test/passt/dhcp4
-rw-r--r--test/passt/ndp6
-rw-r--r--test/passt_in_ns/dhcp4
-rw-r--r--test/pasta/dhcp4
-rw-r--r--test/pasta/ndp6
-rw-r--r--test/pasta_options/log_to_file10
-rw-r--r--test/perf/passt_tcp6
-rw-r--r--test/perf/pasta_tcp2
-rw-r--r--test/perf/pasta_udp10
-rwxr-xr-xtest/prepare-distro-img.sh4
-rwxr-xr-xtest/run24
-rwxr-xr-xtest/smoke/smoke.sh33
-rw-r--r--test/two_guests/basic6
37 files changed, 452 insertions, 211 deletions
diff --git a/test/.gitignore b/test/.gitignore
index 3573444..9412f0d 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -11,3 +11,5 @@ nstool
rampstream
guest-key
guest-key.pub
+/exeter/
+*.bats
diff --git a/test/Makefile b/test/Makefile
index bf63db8..6ed233a 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -5,7 +5,11 @@
# Copyright Red Hat
# Author: David Gibson <david@gibson.dropbear.id.au>
+BATS = bats -j $(shell nproc)
+EXETOOL = exeter/exetool/exetool
WGET = wget -c
+FLAKE8 = flake8
+MYPY = mypy --strict
DEBIAN_IMGS = debian-8.11.0-openstack-amd64.qcow2 \
debian-10-nocloud-amd64.qcow2 \
@@ -13,7 +17,7 @@ DEBIAN_IMGS = debian-8.11.0-openstack-amd64.qcow2 \
debian-10-generic-ppc64el-20220911-1135.qcow2 \
debian-11-nocloud-amd64.qcow2 \
debian-11-generic-arm64.qcow2 \
- debian-11-generic-ppc64el.qcow2 \
+ debian-11-generic-ppc64el-20250703-2162.qcow2 \
debian-sid-nocloud-amd64-daily.qcow2 \
debian-sid-nocloud-arm64-daily.qcow2 \
debian-sid-nocloud-ppc64el-daily.qcow2
@@ -50,18 +54,27 @@ UBUNTU_NEW_IMGS = xenial-server-cloudimg-powerpc-disk1.img \
jammy-server-cloudimg-s390x.img
UBUNTU_IMGS = $(UBUNTU_OLD_IMGS) $(UBUNTU_NEW_IMGS)
-DOWNLOAD_ASSETS = mbuto podman \
+DOWNLOAD_ASSETS = $(EXETOOL) mbuto podman \
$(DEBIAN_IMGS) $(FEDORA_IMGS) $(OPENSUSE_IMGS) $(UBUNTU_IMGS)
TESTDATA_ASSETS = small.bin big.bin medium.bin \
rampstream
LOCAL_ASSETS = mbuto.img mbuto.mem.img podman/bin/podman QEMU_EFI.fd \
$(DEBIAN_IMGS:%=prepared-%) $(FEDORA_IMGS:%=prepared-%) \
$(UBUNTU_NEW_IMGS:%=prepared-%) \
- nstool guest-key guest-key.pub \
- $(TESTDATA_ASSETS)
+ nstool guest-key guest-key.pub $(TESTDATA_ASSETS)
ASSETS = $(DOWNLOAD_ASSETS) $(LOCAL_ASSETS)
+EXETER_PYPATH = exeter/py3
+EXETER_PYTHON = build/build.py
+EXETER_BATS = smoke/smoke.sh.bats \
+ $(EXETER_PYTHON:%=%.bats) build/static_checkers.sh.bats
+BATS_FILES = $(EXETER_BATS) \
+ podman/test/system/505-networking-pasta.bats
+
+# Python test code (for linters)
+PYPKGS = $(EXETER_PYTHON)
+
CFLAGS = -Wall -Werror -Wextra -pedantic -std=c99
assets: $(ASSETS)
@@ -70,6 +83,11 @@ assets: $(ASSETS)
pull-%: %
git -C $* pull
+exeter:
+ git clone https://gitlab.com/dgibson/exeter.git
+
+exeter/exetool/exetool: pull-exeter
+
mbuto:
git clone git://mbuto.sh/mbuto
@@ -115,6 +133,18 @@ medium.bin:
big.bin:
dd if=/dev/urandom bs=1M count=10 of=$@
+flake8: pull-exeter
+ PYTHONPATH=$(EXETER_PYPATH) $(FLAKE8) $(PYPKGS)
+
+mypy: pull-exeter
+ PYTHONPATH=$(EXETER_PYPATH) $(MYPY) $(PYPKGS)
+
+$(EXETER_BATS): %.bats: % $(EXETOOL)
+ PYTHONPATH=$(EXETER_PYPATH) $(EXETOOL) bats -- $< > $@
+
+bats: $(BATS_FILES) pull-podman
+ PYTHONPATH=$(EXETER_PYPATH) CONTAINERS_HELPER_BINARY_DIR=.. $(BATS) $(BATS_FILES)
+
check: assets
./run
@@ -123,7 +153,9 @@ debug: assets
clean:
rm -f perf.js *~
+ rm -rf .mypy_cache
rm -f $(LOCAL_ASSETS)
+ rm -f $(EXETER_BATS)
rm -rf test_logs
rm -f prepared-*.qcow2 prepared-*.img
@@ -132,79 +164,82 @@ realclean: clean
# Debian downloads
debian-8.11.0-openstack-%.qcow2:
- $(WGET) -O $@ https://cloud.debian.org/images/cloud/OpenStack/archive/8.11.0/debian-8.11.0-openstack-$*.qcow2
+ -$(WGET) -O $@ https://cloud.debian.org/images/cloud/OpenStack/archive/8.11.0/debian-8.11.0-openstack-$*.qcow2
debian-10-nocloud-%.qcow2:
- $(WGET) -O $@ https://cloud.debian.org/images/cloud/buster/latest/debian-10-nocloud-$*.qcow2
+ -$(WGET) -O $@ https://cloud.debian.org/images/cloud/buster/latest/debian-10-nocloud-$*.qcow2
debian-10-generic-ppc64el-20220911-1135.qcow2:
- $(WGET) -O $@ https://cloud.debian.org/images/cloud/buster/20220911-1135/debian-10-generic-ppc64el-20220911-1135.qcow2
+ -$(WGET) -O $@ https://cloud.debian.org/images/cloud/buster/20220911-1135/debian-10-generic-ppc64el-20220911-1135.qcow2
debian-10-generic-%.qcow2:
- $(WGET) -O $@ https://cloud.debian.org/images/cloud/buster/latest/debian-10-generic-$*.qcow2
+ -$(WGET) -O $@ https://cloud.debian.org/images/cloud/buster/latest/debian-10-generic-$*.qcow2
debian-11-nocloud-%.qcow2:
- $(WGET) -O $@ https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-nocloud-$*.qcow2
+ -$(WGET) -O $@ https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-nocloud-$*.qcow2
debian-11-generic-%.qcow2:
- $(WGET) -O $@ https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-generic-$*.qcow2
+ -$(WGET) -O $@ https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-generic-$*.qcow2
+
+debian-11-generic-ppc64el-20250703-2162.qcow2:
+ -$(WGET) -O $@ https://cloud.debian.org/images/cloud/bullseye/20250703-2162/debian-11-generic-ppc64el-20250703-2162.qcow2
debian-sid-nocloud-%-daily.qcow2:
- $(WGET) -O $@ https://cloud.debian.org/images/cloud/sid/daily/latest/debian-sid-nocloud-$*-daily.qcow2
+ -$(WGET) -O $@ https://cloud.debian.org/images/cloud/sid/daily/latest/debian-sid-nocloud-$*-daily.qcow2
# Fedora downloads
Fedora-Cloud-Base-26-1.5.%.qcow2:
- $(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/26/CloudImages/$*/images/Fedora-Cloud-Base-26-1.5.$*.qcow2
+ -$(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/26/CloudImages/$*/images/Fedora-Cloud-Base-26-1.5.$*.qcow2
Fedora-Cloud-Base-27-1.6.%.qcow2:
- $(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/27/CloudImages/$*/images/Fedora-Cloud-Base-27-1.6.$*.qcow2
+ -$(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/27/CloudImages/$*/images/Fedora-Cloud-Base-27-1.6.$*.qcow2
Fedora-Cloud-Base-28-1.1.%.qcow2:
- $(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/28/Cloud/$*/images/Fedora-Cloud-Base-28-1.1.$*.qcow2
+ -$(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/28/Cloud/$*/images/Fedora-Cloud-Base-28-1.1.$*.qcow2
Fedora-Cloud-Base-29-1.2.%.qcow2:
- $(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/29/Cloud/$*/images/Fedora-Cloud-Base-29-1.2.$*.qcow2
+ -$(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/29/Cloud/$*/images/Fedora-Cloud-Base-29-1.2.$*.qcow2
Fedora-Cloud-Base-30-1.2.%.qcow2:
- $(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/30/Cloud/$*/images/Fedora-Cloud-Base-30-1.2.$*.qcow2
+ -$(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/30/Cloud/$*/images/Fedora-Cloud-Base-30-1.2.$*.qcow2
Fedora-Cloud-Base-31-1.9.%.qcow2:
- $(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/31/Cloud/$*/images/Fedora-Cloud-Base-31-1.9.$*.qcow2
+ -$(WGET) -O $@ http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/31/Cloud/$*/images/Fedora-Cloud-Base-31-1.9.$*.qcow2
Fedora-Cloud-Base-32-1.6.%.qcow2:
- $(WGET) -O $@ https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/32/Cloud/$*/images/Fedora-Cloud-Base-32-1.6.$*.qcow2
+ -$(WGET) -O $@ https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/32/Cloud/$*/images/Fedora-Cloud-Base-32-1.6.$*.qcow2
Fedora-Cloud-Base-33-1.2.%.qcow2:
- $(WGET) -O $@ https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/33/Cloud/$*/images/Fedora-Cloud-Base-33-1.2.$*.qcow2
+ -$(WGET) -O $@ https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/33/Cloud/$*/images/Fedora-Cloud-Base-33-1.2.$*.qcow2
Fedora-Cloud-Base-34-1.2.%.qcow2:
- $(WGET) -O $@ https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/34/Cloud/$*/images/Fedora-Cloud-Base-34-1.2.$*.qcow2
+ -$(WGET) -O $@ https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/34/Cloud/$*/images/Fedora-Cloud-Base-34-1.2.$*.qcow2
Fedora-Cloud-Base-35-1.2.%.qcow2:
- $(WGET) -O $@ https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/35/Cloud/$*/images/Fedora-Cloud-Base-35-1.2.$*.qcow2
+ -$(WGET) -O $@ https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/35/Cloud/$*/images/Fedora-Cloud-Base-35-1.2.$*.qcow2
# OpenSuSE downloads
openSUSE-Leap-15.1-JeOS.x86_64-kvm-and-xen.qcow2:
- $(WGET) -O $@ https://download.opensuse.org/distribution/leap/15.1/jeos/openSUSE-Leap-15.1-JeOS.x86_64-kvm-and-xen.qcow2
+ -$(WGET) -O $@ https://download.opensuse.org/distribution/leap/15.1/jeos/openSUSE-Leap-15.1-JeOS.x86_64-kvm-and-xen.qcow2
openSUSE-Leap-15.2-JeOS.x86_64-kvm-and-xen.qcow2:
- $(WGET) -O $@ https://download.opensuse.org/distribution/leap/15.2/appliances/openSUSE-Leap-15.2-JeOS.x86_64-kvm-and-xen.qcow2
+ -$(WGET) -O $@ https://download.opensuse.org/distribution/leap/15.2/appliances/openSUSE-Leap-15.2-JeOS.x86_64-kvm-and-xen.qcow2
openSUSE-Leap-15.3-JeOS.x86_64-kvm-and-xen.qcow2:
- $(WGET) -O $@ https://download.opensuse.org/distribution/leap/15.3/appliances/openSUSE-Leap-15.3-JeOS.x86_64-kvm-and-xen.qcow2
+ -$(WGET) -O $@ https://download.opensuse.org/distribution/leap/15.3/appliances/openSUSE-Leap-15.3-JeOS.x86_64-kvm-and-xen.qcow2
openSUSE-Tumbleweed-ARM-JeOS-efi.aarch64.raw.xz:
- $(WGET) -O $@ http://download.opensuse.org/ports/aarch64/tumbleweed/appliances/openSUSE-Tumbleweed-ARM-JeOS-efi.aarch64.raw.xz
+ -$(WGET) -O $@ http://download.opensuse.org/ports/aarch64/tumbleweed/appliances/openSUSE-Tumbleweed-ARM-JeOS-efi.aarch64.raw.xz
openSUSE-Tumbleweed-ARM-JeOS-efi.armv7l.raw.xz:
- $(WGET) -O $@ http://download.opensuse.org/ports/armv7hl/tumbleweed/appliances/openSUSE-Tumbleweed-ARM-JeOS-efi.armv7l.raw.xz
+ -$(WGET) -O $@ http://download.opensuse.org/ports/armv7hl/tumbleweed/appliances/openSUSE-Tumbleweed-ARM-JeOS-efi.armv7l.raw.xz
# Ubuntu downloads
trusty-server-cloudimg-%-disk1.img:
- $(WGET) -O $@ https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-$*-disk1.img
+ -$(WGET) -O $@ https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-$*-disk1.img
xenial-server-cloudimg-powerpc-disk1.img:
- $(WGET) -O $@ https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-powerpc-disk1.img
+ -$(WGET) -O $@ https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-powerpc-disk1.img
jammy-server-cloudimg-s390x.img:
- $(WGET) -O $@ https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-s390x.img
+ -$(WGET) -O $@ https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-s390x.img
diff --git a/test/README.md b/test/README.md
index 91ca603..0df7533 100644
--- a/test/README.md
+++ b/test/README.md
@@ -32,7 +32,7 @@ Example for Debian, and possibly most Debian-based distributions:
git go iperf3 isc-dhcp-common jq libgpgme-dev libseccomp-dev linux-cpupower
lm-sensors lz4 netavark netcat-openbsd psmisc qemu-efi-aarch64
qemu-system-arm qemu-system-misc qemu-system-ppc qemu-system-x86
- qemu-system-x86 sipcalc socat strace tmux uidmap valgrind
+ sipcalc socat strace tmux uidmap valgrind
NOTE: the tests need a qemu version >= 7.2, or one that contains commit
13c6be96618c ("net: stream: add unix socket"): this change introduces support
@@ -81,7 +81,12 @@ The following additional packages are commonly needed:
## Regular test
-Just issue:
+Before running the tests, you need to prepare the required assets:
+
+ cd test
+ make assets
+
+Then issue:
./run
@@ -91,6 +96,32 @@ variable settings: DEBUG=1 enables debugging messages, TRACE=1 enables tracing
PCAP=1 TRACE=1 ./run
+**Note:**
+
+* Don't run the tests as root, the whole point of passt is not to run as root.
+
+* If you switch users before running the tests, you may hit "Permission denied"
+ error. It's probably due to
+ [Bug 967509](https://bugzilla.redhat.com/show_bug.cgi?id=967509).
+ If you switch users with `su` or `sudo`, the directory `/run/user/ID` may
+ not be created, and `XDG_RUNTIME_DIR` points to the /run/user directory of
+ the previous user rather than the target user.
+
+ **Workaround:** Log out and log back in as the intended user to ensure the
+ correct runtime directory is set up. Or use `machinectl shell --uid=$user`.
+
+* SELinux may prevent the tests from running correctly. To avoid this,
+ temporarily disable it by running:
+
+ setenforce 0
+
+* Some tests require a QEMU version >= 10.0.0, or a build that includes the
+ following commits:
+
+ 60f543ad917f ("virtio-net: vhost-user: Implement internal migration")
+ 3f65357313e0 ("vhost: Add stubs for the migration state transfer
+ interface")
+
## Running selected tests
Rudimentary support to run a list of selected tests, without support for
diff --git a/test/build/all b/test/build/all
deleted file mode 100644
index 1f79e0d..0000000
--- a/test/build/all
+++ /dev/null
@@ -1,61 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-#
-# PASST - Plug A Simple Socket Transport
-# for qemu/UNIX domain socket mode
-#
-# PASTA - Pack A Subtle Tap Abstraction
-# for network namespace/tap device mode
-#
-# test/build/all - Build targets, one by one, then all together, check output
-#
-# Copyright (c) 2021 Red Hat GmbH
-# Author: Stefano Brivio <sbrivio@redhat.com>
-
-htools make cc rm uname getconf mkdir cp rm man
-
-test Build passt
-host make clean
-check ! [ -e passt ]
-host CFLAGS="-Werror" make passt
-check [ -f passt ]
-
-test Build pasta
-host make clean
-check ! [ -e pasta ]
-host CFLAGS="-Werror" make pasta
-check [ -h pasta ]
-
-test Build qrap
-host make clean
-check ! [ -e qrap ]
-host CFLAGS="-Werror" make qrap
-check [ -f qrap ]
-
-test Build all
-host make clean
-check ! [ -e passt ]
-check ! [ -e pasta ]
-check ! [ -e qrap ]
-host CFLAGS="-Werror" make
-check [ -f passt ]
-check [ -h pasta ]
-check [ -f qrap ]
-
-test Install
-host mkdir __STATEDIR__/prefix
-host prefix=__STATEDIR__/prefix make install
-check [ -f __STATEDIR__/prefix/bin/passt ]
-check [ -h __STATEDIR__/prefix/bin/pasta ]
-check [ -f __STATEDIR__/prefix/bin/qrap ]
-check man -M __STATEDIR__/prefix/share/man -W passt
-check man -M __STATEDIR__/prefix/share/man -W pasta
-check man -M __STATEDIR__/prefix/share/man -W qrap
-
-test Uninstall
-host prefix=__STATEDIR__/prefix make uninstall
-check ! [ -f __STATEDIR__/prefix/bin/passt ]
-check ! [ -h __STATEDIR__/prefix/bin/pasta ]
-check ! [ -f __STATEDIR__/prefix/bin/qrap ]
-check ! man -M __STATEDIR__/prefix/share/man -W passt 2>/dev/null
-check ! man -M __STATEDIR__/prefix/share/man -W pasta 2>/dev/null
-check ! man -M __STATEDIR__/prefix/share/man -W qrap 2>/dev/null
diff --git a/test/build/build.py b/test/build/build.py
new file mode 100755
index 0000000..e3de830
--- /dev/null
+++ b/test/build/build.py
@@ -0,0 +1,110 @@
+#! /usr/bin/env python3
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# PASST - Plug A Simple Socket Transport
+# for qemu/UNIX domain socket mode
+#
+# PASTA - Pack A Subtle Tap Abstraction
+# for network namespace/tap device mode
+#
+# test/build/build.py - Test build and install targets
+#
+# Copyright Red Hat
+# Author: David Gibson <david@gibson.dropbear.id.au>
+
+import contextlib
+import os
+from pathlib import Path
+import subprocess
+import tempfile
+from typing import Iterator
+
+import exeter
+
+
+def sh(cmd: str) -> None:
+ """Run given command in a shell"""
+ subprocess.run(cmd, shell=True)
+
+
+@contextlib.contextmanager
+def clone_sources() -> Iterator[str]:
+ """Create a temporary copy of the passt sources.
+
+ When the context enters create a temporary directory and copy the
+ passt sources into it. Clean it up when the context exits.
+ """
+
+ os.chdir('..') # Move from test/ to repo base
+ with tempfile.TemporaryDirectory(ignore_cleanup_errors=False) as tmpdir:
+ sh(f"cp --parents -d $(git ls-files) {tmpdir}")
+ os.chdir(tmpdir)
+ yield tmpdir
+
+
+def test_make(target: str, expected_files: list[str]) -> None:
+ """Test `make {target}`
+
+ Arguments:
+ target -- make target to invoke
+ expected_files -- files make is expected to create
+
+ Verifies that
+ 1) `make target` completes successfully
+ 2) expected_files care created by `make target`
+ 3) expected_files are removed by `make clean`
+ """
+
+ ex_paths = [Path(f) for f in expected_files]
+ with clone_sources():
+ for p in ex_paths:
+ assert not p.exists(), f"{p} existed before make"
+ sh(f'make {target} CFLAGS="-Werror"')
+ for p in ex_paths:
+ assert p.exists(), f"{p} wasn't made"
+ sh('make clean')
+ for p in ex_paths:
+ assert not p.exists(), f"{p} existed after make clean"
+
+
+exeter.register('make_passt', test_make, 'passt', ['passt'])
+exeter.register('make_pasta', test_make, 'pasta', ['pasta'])
+exeter.register('make_qrap', test_make, 'qrap', ['qrap'])
+exeter.register('make_all', test_make, 'all', ['passt', 'pasta', 'qrap'])
+
+
+@exeter.test
+def test_install_uninstall() -> None:
+ """Test `make install` and `make uninstall`
+
+ Tests that `make install` installs the expected files to the
+ install prefix, and that `make uninstall` removes them again.
+ """
+
+ with clone_sources():
+ with tempfile.TemporaryDirectory(ignore_cleanup_errors=False) \
+ as prefix:
+ bindir = Path(prefix) / 'bin'
+ mandir = Path(prefix) / 'share/man'
+ progs = ['passt', 'pasta', 'qrap']
+
+ # Install
+ sh(f'make install CFLAGS="-Werror" prefix={prefix}')
+
+ for prog in progs:
+ exe = bindir / prog
+ assert exe.is_file(), f"{exe} does not exist as a regular file"
+ sh(f'man -M {mandir} -W {prog}')
+
+ # Uninstall
+ sh(f'make uninstall prefix={prefix}')
+
+ for prog in progs:
+ exe = bindir / prog
+ assert not exe.exists(), f"{exe} exists after uninstall"
+ sh(f'! man -M {mandir} -W {prog}')
+
+
+if __name__ == '__main__':
+ exeter.main()
diff --git a/test/build/clang_tidy b/test/build/clang_tidy
deleted file mode 100644
index 40573bf..0000000
--- a/test/build/clang_tidy
+++ /dev/null
@@ -1,17 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-#
-# PASST - Plug A Simple Socket Transport
-# for qemu/UNIX domain socket mode
-#
-# PASTA - Pack A Subtle Tap Abstraction
-# for network namespace/tap device mode
-#
-# test/build/clang_tidy - Run source through clang-tidy(1) linter
-#
-# Copyright (c) 2021 Red Hat GmbH
-# Author: Stefano Brivio <sbrivio@redhat.com>
-
-htools clang-tidy
-
-test Run clang-tidy
-host make clang-tidy
diff --git a/test/build/cppcheck b/test/build/cppcheck
deleted file mode 100644
index 0e1dbce..0000000
--- a/test/build/cppcheck
+++ /dev/null
@@ -1,17 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-#
-# PASST - Plug A Simple Socket Transport
-# for qemu/UNIX domain socket mode
-#
-# PASTA - Pack A Subtle Tap Abstraction
-# for network namespace/tap device mode
-#
-# test/build/cppcheck - Run source through cppcheck(1) linter
-#
-# Copyright (c) 2021 Red Hat GmbH
-# Author: Stefano Brivio <sbrivio@redhat.com>
-
-htools cppcheck
-
-test Run cppcheck
-host make cppcheck
diff --git a/test/build/static_checkers.sh b/test/build/static_checkers.sh
new file mode 100755
index 0000000..96679fb
--- /dev/null
+++ b/test/build/static_checkers.sh
@@ -0,0 +1,42 @@
+#! /bin/sh
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# PASST - Plug A Simple Socket Transport
+# for qemu/UNIX domain socket mode
+#
+# PASTA - Pack A Subtle Tap Abstraction
+# for network namespace/tap device mode
+#
+# test/build/static_checkers.sh - Run static checkers
+#
+# Copyright Red Hat
+# Author: David Gibson <david@gibson.dropbear.id.au>
+
+. $(dirname ${0})/../exeter/sh/exeter.sh
+
+# do_check() - Run static checker as a test if the binary is available
+# $1: Static checker (uased as both executable name and make target)
+# $@: Any additional arguments required to make
+do_check() {
+ checker="${1}"
+ shift
+ if ! which "${checker}" >/dev/null 2>/dev/null; then
+ exeter_skip "${checker} not available"
+ fi
+ make "${@}" "${checker}"
+}
+
+exeter_register cppcheck do_check cppcheck -C ..
+exeter_set_description cppcheck "passt sources pass cppcheck"
+
+exeter_register clang_tidy do_check clang-tidy -C ..
+exeter_set_description clang_tidy "passt sources pass clang-tidy"
+
+exeter_register flake8 do_check flake8
+exeter_set_description flake8 "passt tests in Python pass flake8"
+
+exeter_register mypy do_check mypy
+exeter_set_description mypy "passt tests in Python pass mypy --strict"
+
+exeter_main "${@}"
diff --git a/test/demo/podman b/test/demo/podman
index edd403a..393691c 100644
--- a/test/demo/podman
+++ b/test/demo/podman
@@ -310,8 +310,8 @@ nl
say Everything is set now, let's start
sleep 2
hout IFNAME ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
-hout ADDR4 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global").local'
-hout ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global").local'
+hout ADDR4 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope != "host" and .scope != "link").local'
+hout ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope != "host" and .scope != "link").local'
hout GW4 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
hout GW6 ip -j -6 route show|jq -rM '.[] | select(.dst == "default").gateway'
diff --git a/test/lib/exeter b/test/lib/exeter
new file mode 100644
index 0000000..ccdb19c
--- /dev/null
+++ b/test/lib/exeter
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# PASST - Plug A Simple Socket Transport
+# for qemu/UNIX domain socket mode
+#
+# PASTA - Pack A Subtle Tap Abstraction
+# for network namespace/tap device mode
+#
+# test/lib/exeter - Run exeter tests within the rest of passt's tests
+#
+# Copyright Red Hat
+# Author: David Gibson <david@gibson.dropbear.id.au>
+
+EXETOOL="${BASEPATH}/exeter/exetool/exetool"
+
+# is_exeter() - Determine if a test file is an exeter program
+# $@: Command line to invoke test program
+is_exeter() {
+ ${EXETOOL} probe -- "${@}"
+}
+
+# exeter() - Run each test in an exeter program, logging each test separately
+# $@: Command line to invoke exeter test program
+exeter() {
+ STATESETUP="${STATEBASE}/${1}"
+ mkdir -p "${STATESETUP}"
+
+ context_setup_host host
+ layout_host
+
+ cd test
+
+ __ntests=$(${EXETOOL} list -- "${@}" | wc -l)
+ if [ ${?} != 0 ]; then
+ info "Failed to get exeter manifest for ${@}"
+ pause_continue \
+ "Press any key to pause test session" \
+ "Resuming in " \
+ "Paused, press any key to continue" \
+ 5
+ return
+ fi
+
+ status_file_start "${*} (exeter)" ${__ntests}
+ [ ${CI} -eq 1 ] && video_link "${1}"
+
+ for __testid in $(${EXETOOL} list -- "${@}"); do
+ __desc="$(${EXETOOL} desc -- "${@}" -- "${__testid}")"
+ status_test_start "${__desc}"
+ status=0
+ context_run host "${*} '${__testid}'" || status="${?}"
+ if [ "${status}" = 0 ]; then
+ status_test_ok
+ elif [ "${status}" = 77 ]; then
+ status_test_skip
+ else
+ status_test_fail
+ fi
+ done
+
+ cd ..
+
+ teardown_context_watch ${PANE_HOST} host
+}
diff --git a/test/lib/setup b/test/lib/setup
index 575bc21..5994598 100755
--- a/test/lib/setup
+++ b/test/lib/setup
@@ -350,7 +350,7 @@ setup_migrate() {
sleep 1
- __opts="--vhost-user"
+ __opts="--vhost-user --migrate-exit --migrate-no-linger"
[ ${PCAP} -eq 1 ] && __opts="${__opts} -p ${LOGDIR}/passt_1.pcap"
[ ${DEBUG} -eq 1 ] && __opts="${__opts} -d"
[ ${TRACE} -eq 1 ] && __opts="${__opts} --trace"
@@ -360,7 +360,7 @@ setup_migrate() {
context_run_bg passt_repair_1 "./passt-repair ${STATESETUP}/passt_1.socket.repair"
- __opts="--vhost-user"
+ __opts="--vhost-user --migrate-exit --migrate-no-linger"
[ ${PCAP} -eq 1 ] && __opts="${__opts} -p ${LOGDIR}/passt_2.pcap"
[ ${DEBUG} -eq 1 ] && __opts="${__opts} -d"
[ ${TRACE} -eq 1 ] && __opts="${__opts} --trace"
diff --git a/test/lib/term b/test/lib/term
index ed690de..f596364 100755
--- a/test/lib/term
+++ b/test/lib/term
@@ -19,6 +19,7 @@ STATUS_FILE_INDEX=0
STATUS_COLS=
STATUS_PASS=0
STATUS_FAIL=0
+STATUS_SKIPPED=0
PR_RED='\033[1;31m'
PR_GREEN='\033[1;32m'
@@ -28,32 +29,32 @@ PR_NC='\033[0m'
PR_DELAY_INIT=100 # ms
# info() - Highlight test log pane, print message to it and to log file
-# $@: Message to print
+# $*: Message to print
info() {
tmux select-pane -t ${PANE_INFO}
- printf "${@}\n" >> $STATEBASE/log_pipe
- printf "${@}\n" >> "${LOGFILE}"
+ printf "%b\n" "${*}" >> $STATEBASE/log_pipe
+ printf "%b\n" "${*}" >> "${LOGFILE}"
}
# info_n() - Highlight, print message to pane and to log file without newline
-# $@: Message to print
+# $*: Message to print
info_n() {
tmux select-pane -t ${PANE_INFO}
- printf "${@}" >> $STATEBASE/log_pipe
- printf "${@}" >> "${LOGFILE}"
+ printf "%b" "${*}" >> $STATEBASE/log_pipe
+ printf "%b" "${*}" >> "${LOGFILE}"
}
# info_nolog() - Highlight test log pane, print message to it
-# $@: Message to print
+# $*: Message to print
info_nolog() {
tmux select-pane -t ${PANE_INFO}
- printf "${@}\n" >> $STATEBASE/log_pipe
+ printf "%b\n" "${*}" >> $STATEBASE/log_pipe
}
# info_nolog() - Print message to log file
-# $@: Message to print
+# $*: Message to print
log() {
- printf "${@}\n" >> "${LOGFILE}"
+ printf "%b\n" "${*}" >> "${LOGFILE}"
}
# info_nolog_n() - Send message to pane without highlighting it, without newline
@@ -362,8 +363,8 @@ status_test_start() {
info_check() {
switch_pane ${PANE_INFO}
- printf "${PR_YELLOW}?${PR_NC} ${@}" >> $STATEBASE/log_pipe
- printf "? ${@}" >> "${LOGFILE}"
+ printf "%b" "${PR_YELLOW}?${PR_NC} ${*}" >> $STATEBASE/log_pipe
+ printf "? %b" "${*}" >> "${LOGFILE}"
}
# info_check_passed() - Display and log a new line when a check passes
@@ -439,19 +440,21 @@ info_layout() {
# status_test_ok() - Update counter of passed tests, log and display message
status_test_ok() {
STATUS_PASS=$((STATUS_PASS + 1))
- tmux set status-right "PASS: ${STATUS_PASS} | FAIL: ${STATUS_FAIL} | #(TZ="UTC" date -Iseconds)"
+ tmux set status-right "PASS: ${STATUS_PASS} | FAIL: ${STATUS_FAIL} | SKIPPED: ${STATUS_SKIPPED} | #(TZ="UTC" date -Iseconds)"
info_passed
}
# status_test_fail() - Update counter of failed tests, log and display message
status_test_fail() {
STATUS_FAIL=$((STATUS_FAIL + 1))
- tmux set status-right "PASS: ${STATUS_PASS} | FAIL: ${STATUS_FAIL} | #(TZ="UTC" date -Iseconds)"
+ tmux set status-right "PASS: ${STATUS_PASS} | FAIL: ${STATUS_FAIL} | SKIPPED: ${STATUS_SKIPPED} | #(TZ="UTC" date -Iseconds)"
info_failed
}
# status_test_fail() - Update counter of failed tests, log and display message
status_test_skip() {
+ STATUS_SKIPPED=$((STATUS_SKIPPED + 1))
+ tmux set status-right "PASS: ${STATUS_PASS} | FAIL: ${STATUS_FAIL} | SKIPPED: ${STATUS_SKIPPED} | #(TZ="UTC" date -Iseconds)"
info_skipped
}
@@ -704,7 +707,7 @@ term() {
tmux set window-status-current-style 'bg=colour1 fg=colour233 bold'
tmux set status-right '#(TZ="UTC" date -Iseconds)'
- tmux set status-right-length 50
+ tmux set status-right-length 64
tmux set status-right-style 'bg=colour1 fg=colour233 bold'
tmux set history-limit 500000
diff --git a/test/lib/test b/test/lib/test
index 758250a..7349674 100755
--- a/test/lib/test
+++ b/test/lib/test
@@ -20,10 +20,7 @@ test_iperf3s() {
__sctx="${1}"
__port="${2}"
- pane_or_context_run_bg "${__sctx}" \
- 'iperf3 -s -p'${__port}' & echo $! > s.pid' \
-
- sleep 1 # Wait for server to be ready
+ pane_or_context_run "${__sctx}" 'iperf3 -s -p'${__port}' -D -I s.pid'
}
# test_iperf3k() - Kill iperf3 server
@@ -31,7 +28,7 @@ test_iperf3s() {
test_iperf3k() {
__sctx="${1}"
- pane_or_context_run "${__sctx}" 'kill -INT $(cat s.pid); rm s.pid'
+ pane_or_context_run "${__sctx}" 'kill -INT $(cat s.pid)'
sleep 1 # Wait for kernel to free up ports
}
diff --git a/test/memory/passt b/test/memory/passt
index 7e45724..c5142ea 100644
--- a/test/memory/passt
+++ b/test/memory/passt
@@ -51,7 +51,7 @@ guest sed /proc/slabinfo -ne 's/^\([^ ]* *[^ ]* *[^ ]* *[^ ]*\).*/\\\1/p' > /tmp
guest kill \$(cat /tmp/pid)
guest diff -y --suppress-common-lines /tmp/meminfo.before /tmp/meminfo.after || :
guest nm -td -Sr --size-sort -P /bin/passt.avx2 | head -30 | tee /tmp/nm.size
-guest sed /proc/slabinfo -ne 's/\(.*<objsize>\).*$/\1/p' | tail -1; (diff -y --suppress-common-lines /tmp/slabinfo.before /tmp/slabinfo.after | sort -grk8)
+guest sed /proc/slabinfo -ne 's/\(.*<objsize>\).*$/\\\1/p' | tail -1; (diff -y --suppress-common-lines /tmp/slabinfo.before /tmp/slabinfo.after | sort -grk8)
endef
def summary
diff --git a/test/migrate/basic b/test/migrate/basic
index 3f11f7d..bab2d76 100644
--- a/test/migrate/basic
+++ b/test/migrate/basic
@@ -39,8 +39,8 @@ guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR1_6__"
test TCP/IPv4: guest1/guest2 > host
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
diff --git a/test/migrate/basic_fin b/test/migrate/basic_fin
index aa61ec5..1d92c92 100644
--- a/test/migrate/basic_fin
+++ b/test/migrate/basic_fin
@@ -39,8 +39,8 @@ guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR1_6__"
test TCP/IPv4: guest1, half-close, guest2 > host
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
diff --git a/test/migrate/iperf3_bidir6 b/test/migrate/iperf3_bidir6
index 4bfefb5..e95eee8 100644
--- a/test/migrate/iperf3_bidir6
+++ b/test/migrate/iperf3_bidir6
@@ -44,8 +44,8 @@ guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR1_6__"
test TCP/IPv6 host <-> guest flood, many flows, during migration
diff --git a/test/migrate/iperf3_in6 b/test/migrate/iperf3_in6
index 16cf504..0e863a4 100644
--- a/test/migrate/iperf3_in6
+++ b/test/migrate/iperf3_in6
@@ -44,8 +44,8 @@ guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR1_6__"
test TCP/IPv6 host to guest throughput during migration
diff --git a/test/migrate/iperf3_many_out6 b/test/migrate/iperf3_many_out6
index 88133f2..179e269 100644
--- a/test/migrate/iperf3_many_out6
+++ b/test/migrate/iperf3_many_out6
@@ -44,8 +44,8 @@ guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR1_6__"
test TCP/IPv6 guest to host flood, many flows, during migration
diff --git a/test/migrate/iperf3_out6 b/test/migrate/iperf3_out6
index 21fbfcd..20e6e95 100644
--- a/test/migrate/iperf3_out6
+++ b/test/migrate/iperf3_out6
@@ -44,8 +44,8 @@ guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR1_6__"
test TCP/IPv6 guest to host throughput during migration
diff --git a/test/migrate/rampstream_in b/test/migrate/rampstream_in
index df333ba..5212dfc 100644
--- a/test/migrate/rampstream_in
+++ b/test/migrate/rampstream_in
@@ -40,8 +40,8 @@ guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR1_6__"
test TCP/IPv4: sequence check, ramps, inbound
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
diff --git a/test/migrate/rampstream_out b/test/migrate/rampstream_out
index 8ed3229..897396d 100644
--- a/test/migrate/rampstream_out
+++ b/test/migrate/rampstream_out
@@ -40,8 +40,8 @@ guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR1_6__"
test TCP/IPv4: sequence check, ramps, outbound
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
diff --git a/test/passt.mbuto b/test/passt.mbuto
index 5e00132..598c254 100755
--- a/test/passt.mbuto
+++ b/test/passt.mbuto
@@ -28,9 +28,12 @@ KMODS="${KMODS:- virtio_net virtio_pci vmw_vsock_virtio_transport}"
LINKS="${LINKS:-
ash,dash,bash /init
- ash,dash,bash /bin/sh}"
+ ash,dash,bash /bin/sh
+ sshd /usr/sbin/sshd
+ dhclient /usr/sbin/dhclient
+ sysctl /usr/sbin/sysctl}"
-DIRS="${DIRS} /tmp /usr/sbin /usr/share /var/log /var/lib /etc/ssh /run/sshd /root/.ssh"
+DIRS="${DIRS} /tmp /usr/sbin /usr/bin /usr/share /var/log /var/lib /etc/ssh /run/sshd /root/.ssh"
COPIES="${COPIES} small.bin,/root/small.bin medium.bin,/root/medium.bin big.bin,/root/big.bin rampstream,/bin/rampstream rampstream-check.sh,/bin/rampstream-check.sh"
@@ -61,7 +64,9 @@ set >> \$LOG
exit 0
EOF
chmod 755 /sbin/dhclient-script
- ln -s /bin /usr/bin
+ mv /bin/* /usr/bin || :
+ rm -rf /bin
+ ln -s /usr/bin /bin
ln -s /run /var/run
:> /etc/fstab
diff --git a/test/passt.mem.mbuto b/test/passt.mem.mbuto
index 532eae0..7554a43 100755
--- a/test/passt.mem.mbuto
+++ b/test/passt.mem.mbuto
@@ -12,7 +12,7 @@
PROGS="${PROGS:-ash,dash,bash chmod ip mount insmod mkdir ln cat chmod modprobe
grep mknod sed chown sleep bc ls ps mount unshare chroot cp kill diff
- head tail sort tr tee cut nm which switch_root}"
+ head tail sort tr tee cut nm which switch_root mv rm}"
KMODS="${KMODS:- dummy}"
@@ -22,12 +22,14 @@ LINKS="${LINKS:-
ash,dash,bash /init
ash,dash,bash /bin/sh}"
-DIRS="${DIRS} /tmp /sbin"
+DIRS="${DIRS} /tmp /sbin /usr/bin"
COPIES="${COPIES} ../passt.avx2,/bin/passt.avx2"
FIXUP="${FIXUP}"'
-ln -s /bin /usr/bin
+mv /bin/* /usr/bin || :
+rm -rf /bin
+ln -s /usr/bin /bin
chmod 777 /tmp
sh +m
'
diff --git a/test/passt/dhcp b/test/passt/dhcp
index 145f1ba..904faab 100644
--- a/test/passt/dhcp
+++ b/test/passt/dhcp
@@ -61,8 +61,8 @@ guest /sbin/dhclient -6 __IFNAME__
# Wait for DAD to complete
guest while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
gout ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR6__"
test DHCPv6: route
gout GW6 ip -j -6 route show|jq -rM '.[] | select(.dst == "default").gateway'
diff --git a/test/passt/ndp b/test/passt/ndp
index 516cd6b..80b72bb 100644
--- a/test/passt/ndp
+++ b/test/passt/ndp
@@ -25,9 +25,9 @@ check [ -n "__IFNAME__" ]
test SLAAC: prefix
gout ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.protocol == "kernel_ra") | .local + "/" + (.prefixlen | tostring)] | .[0]'
gout PREFIX6 sipcalc __ADDR6__ | grep prefix | cut -d' ' -f4
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-hout HOST_PREFIX6 sipcalc __HOST_ADDR6__/64 | grep prefix | cut -d' ' -f4
-check [ "__PREFIX6__" = "__HOST_PREFIX6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join("/64 ")'
+hout HOST_PREFIX6 sipcalc __HOST_ADDR6__/64 | grep prefix | cut -d' ' -f4 | tr '\n' ' '
+check echo "__HOST_PREFIX6__" | grep -wq "__PREFIX6__"
test SLAAC: route
gout GW6 ip -j -6 route show|jq -rM '.[] | select(.dst == "default").gateway'
diff --git a/test/passt_in_ns/dhcp b/test/passt_in_ns/dhcp
index a38a690..9d89049 100644
--- a/test/passt_in_ns/dhcp
+++ b/test/passt_in_ns/dhcp
@@ -55,8 +55,8 @@ guest /sbin/dhclient -6 __IFNAME__
# Wait for DAD to complete
guest while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
gout ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(",")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR6__"
test DHCPv6: route
gout GW6 ip -j -6 route show|jq -rM '.[] | select(.dst == "default").gateway'
diff --git a/test/pasta/dhcp b/test/pasta/dhcp
index d4f3ad5..366935f 100644
--- a/test/pasta/dhcp
+++ b/test/pasta/dhcp
@@ -39,8 +39,8 @@ ns /sbin/dhclient -6 --no-pid __IFNAME__
ns while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
nsout ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ __ADDR6__ = __HOST_ADDR6__ ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR6__"
test DHCPv6: route
nsout GW6 ip -j -6 route show|jq -rM '.[] | select(.dst == "default").gateway'
diff --git a/test/pasta/ndp b/test/pasta/ndp
index 952c1ea..1d385c7 100644
--- a/test/pasta/ndp
+++ b/test/pasta/ndp
@@ -24,9 +24,9 @@ ns while ! ip -j -6 addr show dev __IFNAME__ | jq -e '.[].addr_info.[] | select(
test SLAAC: prefix
nsout ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.protocol == "kernel_ra") | .local + "/" + (.prefixlen | tostring)] | .[0]'
nsout PREFIX6 sipcalc __ADDR6__ | grep prefix | cut -d' ' -f4
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM ['.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-hout HOST_PREFIX6 sipcalc __HOST_ADDR6__/64 | grep prefix | cut -d' ' -f4
-check [ "__PREFIX6__" = "__HOST_PREFIX6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM ['.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local]| join("/64 ")'
+hout HOST_PREFIX6 sipcalc __HOST_ADDR6__/64 | grep prefix | cut -d' ' -f4 | tr '\n' ' '
+check echo "__HOST_PREFIX6__" | grep -wq "__PREFIX6__"
test SLAAC: route
nsout GW6 ip -j -6 route show|jq -rM '.[] | select(.dst == "default").gateway'
diff --git a/test/pasta_options/log_to_file b/test/pasta_options/log_to_file
index 3ead06c..db78b04 100644
--- a/test/pasta_options/log_to_file
+++ b/test/pasta_options/log_to_file
@@ -30,19 +30,19 @@ endef
test Log creation
-set PORTS -t 10001,10002 -u 10001,10002
+set PORTS -t 10001,10002 -u 10001,10002 -T none -U none
set LOG_FILE __STATEDIR__/pasta.log
-passt ./pasta -l __LOG_FILE__ -- /bin/true
+passt ./pasta __PORTS__ -l __LOG_FILE__ -- /bin/true
check [ -s __LOG_FILE__ ]
test Log truncated on creation
-passt ./pasta -l __LOG_FILE__ -- /bin/true & wait
+passt ./pasta __PORTS__ -l __LOG_FILE__ -- /bin/true & wait
pout PID2 echo $!
check head -1 __LOG_FILE__ | grep '^pasta .* [(]__PID2__[)]$'
test Maximum log size
-passtb ./pasta --config-net -d -f -l __LOG_FILE__ --log-size $((100 * 1024)) -- sh -c 'while true; do tcp_crr --nolog -l1 -P 10001 -C 10002 -6; done'
+passtb ./pasta __PORTS__ --config-net -d -f -l __LOG_FILE__ --log-size $((100 * 1024)) -- sh -c 'while true; do tcp_crr --nolog -l1 -P 10001 -C 10002 -6; done'
sleep 1
flood_log_client
@@ -67,7 +67,7 @@ passt unshare -rUm
passt mkdir __STATEDIR__/t
passt mount -t tmpfs none __STATEDIR__/t
set LOG_FILE __STATEDIR__/t/log
-passt ./pasta --config-net -d -l __LOG_FILE__ --log-size $((100 * 1024))
+passt ./pasta __PORTS__ --config-net -d -l __LOG_FILE__ --log-size $((100 * 1024))
flood_log_server
flood_log_client
diff --git a/test/perf/passt_tcp b/test/perf/passt_tcp
index 5978c49..1a97a63 100644
--- a/test/perf/passt_tcp
+++ b/test/perf/passt_tcp
@@ -87,7 +87,7 @@ lat -
lat -
nsb tcp_crr --nolog -6
gout LAT tcp_crr --nolog -l1 -6 -c -H __MAP_NS6__ | sed -n 's/^throughput=\(.*\)/\1/p'
-lat __LAT__ 500 400
+lat __LAT__ 550 450
tr TCP throughput over IPv4: guest to host
iperf3s ns 10002
@@ -137,7 +137,7 @@ lat -
lat -
nsb tcp_crr --nolog -4
gout LAT tcp_crr --nolog -l1 -4 -c -H __MAP_NS4__ | sed -n 's/^throughput=\(.*\)/\1/p'
-lat __LAT__ 500 400
+lat __LAT__ 550 450
tr TCP throughput over IPv6: host to guest
iperf3s guest 10001
@@ -208,6 +208,6 @@ lat -
guestb tcp_crr --nolog -P 10001 -C 10011 -4
sleep 1
nsout LAT tcp_crr --nolog -l1 -P 10001 -C 10011 -4 -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p'
-lat __LAT__ 500 300
+lat __LAT__ 500 350
te
diff --git a/test/perf/pasta_tcp b/test/perf/pasta_tcp
index bc0de3c..496d0fe 100644
--- a/test/perf/pasta_tcp
+++ b/test/perf/pasta_tcp
@@ -211,7 +211,7 @@ tr TCP throughput over IPv6: host to ns
iperf3s ns 10002
nsout IFNAME ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
-nsout ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global").local] | .[0]'
+nsout ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope != "host" and .scope != "link").local] | .[0]'
bw -
bw -
bw -
diff --git a/test/perf/pasta_udp b/test/perf/pasta_udp
index ab2f3e8..c51bb6c 100644
--- a/test/perf/pasta_udp
+++ b/test/perf/pasta_udp
@@ -39,7 +39,7 @@ iperf3s host 10003
# (datagram size) = (packet size) - 48: 40 bytes of IPv6 header, 8 of UDP header
iperf3 BW ns ::1 10003 __TIME__ __OPTS__ -b 5G -l 1452
-bw __BW__ 1.0 1.5
+bw __BW__ 0.8 1.2
iperf3 BW ns ::1 10003 __TIME__ __OPTS__ -b 10G -l 3972
bw __BW__ 1.2 1.8
iperf3 BW ns ::1 10003 __TIME__ __OPTS__ -b 30G -l 16336
@@ -64,7 +64,7 @@ iperf3s host 10003
# (datagram size) = (packet size) - 28: 20 bytes of IPv4 header, 8 of UDP header
iperf3 BW ns 127.0.0.1 10003 __TIME__ __OPTS__ -b 5G -l 1372
-bw __BW__ 1.0 1.5
+bw __BW__ 0.8 1.2
iperf3 BW ns 127.0.0.1 10003 __TIME__ __OPTS__ -b 10G -l 3972
bw __BW__ 1.2 1.8
iperf3 BW ns 127.0.0.1 10003 __TIME__ __OPTS__ -b 30G -l 16356
@@ -88,7 +88,7 @@ tr UDP throughput over IPv6: host to ns
iperf3s ns 10002
iperf3 BW host ::1 10002 __TIME__ __OPTS__ -b 5G -l 1452
-bw __BW__ 1.0 1.5
+bw __BW__ 0.8 1.2
iperf3 BW host ::1 10002 __TIME__ __OPTS__ -b 10G -l 3972
bw __BW__ 1.2 1.8
iperf3 BW host ::1 10002 __TIME__ __OPTS__ -b 30G -l 16336
@@ -111,7 +111,7 @@ lat __LAT__ 200 150
tr UDP throughput over IPv4: host to ns
iperf3s ns 10002
iperf3 BW host 127.0.0.1 10002 __TIME__ __OPTS__ -b 5G -l 1372
-bw __BW__ 1.0 1.5
+bw __BW__ 0.8 1.2
iperf3 BW host 127.0.0.1 10002 __TIME__ __OPTS__ -b 10G -l 3972
bw __BW__ 1.2 1.8
iperf3 BW host 127.0.0.1 10002 __TIME__ __OPTS__ -b 30G -l 16356
@@ -196,7 +196,7 @@ tr UDP throughput over IPv6: host to ns
iperf3s ns 10002
nsout IFNAME ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
-nsout ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global").local] | .[0]'
+nsout ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope != "host" and .scope != "link").local] | .[0]'
iperf3 BW host __ADDR6__ 10002 __TIME__ __OPTS__ -b 8G -l 1472
bw __BW__ 0.3 0.5
iperf3 BW host __ADDR6__ 10002 __TIME__ __OPTS__ -b 12G -l 3972
diff --git a/test/prepare-distro-img.sh b/test/prepare-distro-img.sh
index 0d967c9..be2386e 100755
--- a/test/prepare-distro-img.sh
+++ b/test/prepare-distro-img.sh
@@ -3,6 +3,10 @@
IMG="$1"
PASST_FILES="$(echo ../*.c ../*.h ../*.sh ../*.1 ../Makefile ../README.md)"
+# This is just a workaround for Fedora and related distributions.
+# Once it gets fixed, we can drop this.
+export LIBGUESTFS_BACKEND=direct
+
virt-edit -a $IMG /lib/systemd/system/serial-getty@.service -e 's/ExecStart=.*/ExecStart=\/sbin\/agetty --autologin root -8 --keep-baud 115200,38400,9600 %I $TERM/g'
guestfish --rw -a $IMG -i <<EOF
diff --git a/test/run b/test/run
index 4e86f30..f858e55 100755
--- a/test/run
+++ b/test/run
@@ -43,6 +43,9 @@ KERNEL=${KERNEL:-"/boot/vmlinuz-$(uname -r)"}
COMMIT="$(git log --oneline --no-decorate -1)"
+# Let exeter tests written in Python find their modules
+export PYTHONPATH=${BASEPATH}/exeter/py3
+
. lib/util
. lib/context
. lib/setup
@@ -53,6 +56,7 @@ COMMIT="$(git log --oneline --no-decorate -1)"
. lib/layout_ugly
. lib/test
. lib/video
+. lib/exeter
# cleanup() - Remove temporary files
cleanup() {
@@ -67,11 +71,9 @@ run() {
perf_init
[ ${CI} -eq 1 ] && video_start ci
- setup build
- test build/all
- test build/cppcheck
- test build/clang_tidy
- teardown build
+ exeter smoke/smoke.sh
+ exeter build/build.py
+ exeter build/static_checkers.sh
setup pasta
test pasta/ndp
@@ -202,7 +204,7 @@ skip_distro() {
perf_finish
[ ${CI} -eq 1 ] && video_stop
- log "PASS: ${STATUS_PASS}, FAIL: ${STATUS_FAIL}"
+ log "PASS: ${STATUS_PASS}, FAIL: ${STATUS_FAIL}, SKIPPED: ${STATUS_SKIPPED}"
pause_continue \
"Press any key to keep test session open" \
@@ -223,6 +225,10 @@ run_selected() {
__setup=
for __test; do
+ if is_exeter "test/${__test}"; then
+ exeter "${__test}"
+ continue
+ fi
# HACK: the migrate tests need the setup repeated for
# each test
if [ "${__test%%/*}" != "${__setup}" -o \
@@ -234,9 +240,9 @@ run_selected() {
test "${__test}"
done
- teardown "${__setup}"
+ [ -n "${__setup}" ] && teardown "${__setup}"
- log "PASS: ${STATUS_PASS}, FAIL: ${STATUS_FAIL}"
+ log "PASS: ${STATUS_PASS}, FAIL: ${STATUS_FAIL}, SKIPPED: ${STATUS_SKIPPED}"
pause_continue \
"Press any key to keep test session open" \
@@ -307,4 +313,4 @@ fi
tail -n1 ${LOGFILE}
echo "Log at ${LOGFILE}"
-exit $(tail -n1 ${LOGFILE} | sed -n 's/.*FAIL: \(.*\)$/\1/p')
+exit $(tail -n1 ${LOGFILE} | sed -n 's/.*FAIL: \(.*\),.*$/\1/p')
diff --git a/test/smoke/smoke.sh b/test/smoke/smoke.sh
new file mode 100755
index 0000000..a642fb9
--- /dev/null
+++ b/test/smoke/smoke.sh
@@ -0,0 +1,33 @@
+#! /bin/sh
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# PASST - Plug A Simple Socket Transport
+# for qemu/UNIX domain socket mode
+#
+# PASTA - Pack A Subtle Tap Abstraction
+# for network namespace/tap device mode
+#
+# test/smoke/smoke.sh - Basic smoke tests
+#
+# Copyright Red Hat
+# Author: David Gibson <david@gibson.dropbear.id.au>
+
+. $(dirname $0)/../exeter/sh/exeter.sh
+
+PASST=$(dirname $0)/../../passt
+PASTA=$(dirname $0)/../../pasta
+
+exeter_register passt_version $PASST --version
+exeter_set_description passt_version "Check passt --version works"
+
+exeter_register pasta_version $PASTA --version
+exeter_set_description pasta_version "Check pasta --version works"
+
+exeter_register passt_help $PASST --help
+exeter_set_description passt_help "Check passt --help works"
+
+exeter_register pasta_help $PASTA --help
+exeter_set_description pasta_help "Check pasta --help works"
+
+exeter_main "$@"
diff --git a/test/two_guests/basic b/test/two_guests/basic
index e2338ff..cb48bce 100644
--- a/test/two_guests/basic
+++ b/test/two_guests/basic
@@ -45,9 +45,9 @@ guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1;
guest2 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
g2out ADDR2_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME2__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
-hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
-check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
-check [ "__ADDR2_6__" = "__HOST_ADDR6__" ]
+hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope != "host" and .scope != "link" and .deprecated != true).local] | join(" ")'
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR1_6__"
+check echo "__HOST_ADDR6__" | grep -wq "__ADDR2_6__"
test TCP/IPv4: guest 1 > guest 2
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'