diff --git a/.gitignore b/.gitignore index 2aafd58e..9f9376ad 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /.ccache /dl /output* +/test/.venv /local.mk diff --git a/.gitmodules b/.gitmodules index 7e113c3c..aaab57c1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "9pm"] path = 9pm url = ../9pm +[submodule "qeneth"] + path = qeneth + url = ../qeneth.git diff --git a/qeneth b/qeneth new file mode 160000 index 00000000..5b45ecf0 --- /dev/null +++ b/qeneth @@ -0,0 +1 @@ +Subproject commit 5b45ecf0d98f60e7be0e463d954ddb142a44d6b6 diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile new file mode 100644 index 00000000..39ecfbc7 --- /dev/null +++ b/test/docker/Dockerfile @@ -0,0 +1,27 @@ +FROM alpine:3.18.0 + +RUN apk add --no-cache \ + e2fsprogs \ + gcc \ + graphviz \ + iproute2 \ + iputils \ + libc-dev \ + libyang-dev \ + python3-dev \ + qemu-img \ + qemu-system-x86_64 \ + ruby-mustache \ + squashfs-tools + +# Alpine's QEMU package does not bundle this for some reason, copied +# from Ubuntu +COPY qemu-ifup /etc + +# Needed to let qeneth find mustache(1) +ENV PATH="${PATH}:/usr/lib/ruby/gems/3.2.0/bin" + +# Install all python packages used by the tests +COPY init-venv.sh /root +COPY pip-requirements.txt /root +RUN ~/init-venv.sh ~/pip-requirements.txt diff --git a/test/docker/init-venv.sh b/test/docker/init-venv.sh new file mode 100755 index 00000000..0ee24e97 --- /dev/null +++ b/test/docker/init-venv.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -e + +python3 -m venv ~/.infix-test-venv +. ~/.infix-test-venv/bin/activate + +python3 -m pip install --upgrade pip +python3 -m pip install -r "$1" diff --git a/test/docker/pip-requirements.txt b/test/docker/pip-requirements.txt new file mode 100644 index 00000000..13364972 --- /dev/null +++ b/test/docker/pip-requirements.txt @@ -0,0 +1,5 @@ +libyang==2.7.1 +netconf_client==2.2.0 +networkx==3.1 +pydot==1.4.2 +pyyaml==5.4.1 diff --git a/test/docker/qemu-ifup b/test/docker/qemu-ifup new file mode 100755 index 00000000..f2f4d5db --- /dev/null +++ b/test/docker/qemu-ifup @@ -0,0 +1,42 @@ +#! /bin/sh +# Script to bring a network (tap) device for qemu up. +# The idea is to add the tap device to the same bridge +# as we have default routing to. + +# in order to be able to find brctl +PATH=$PATH:/sbin:/usr/sbin +ip=$(which ip) + +if [ -n "$ip" ]; then + ip link set "$1" up +else + brctl=$(which brctl) + if [ ! "$ip" -o ! "$brctl" ]; then + echo "W: $0: not doing any bridge processing: neither ip nor brctl utility not found" >&2 + exit 0 + fi + ifconfig "$1" 0.0.0.0 up +fi + +switch=$(ip route ls | \ + awk '/^default / { + for(i=0;i&2 diff --git a/test/env b/test/env new file mode 100755 index 00000000..0e37d935 --- /dev/null +++ b/test/env @@ -0,0 +1,128 @@ +#!/bin/sh + +set -e + +INFIX_TEST=ghcr.io/kernelkit/infix-test:0.2 + +extract_yang() +{ + local imgfile="$1" + + [ "$imgfile" ] || { true && return; } + + unsquashfs -q -n -f \ + -d $envdir/yangdir \ + "$imgfile" \ + /usr/share/yang + + INFAMY_ARGS="$INFAMY_ARGS -y $envdir/yangdir" +} + +start_topology() +{ + local qenethdir="$1" + local imgfile="$2" + + [ "$qenethdir" ] || { true && return; } + + rm -rf $envdir/qeneth + cp -a "$qenethdir" $envdir/qeneth + if [ "$imgfile" ]; then + local imgname=$(basename "$imgfile") + imgfile=$(readlink -f "$imgfile") + + ln -sf "$imgfile" $envdir/qeneth/"$imgname" + fi + + (cd $envdir/qeneth/ && $qeneth generate && $qeneth start) + INFAMY_ARGS="$INFAMY_ARGS $envdir/qeneth/topology.dot" +} + +usage() +{ + cat <] [-q ] [...] + + Run in a pre-packaged container with all the packages + required for running the test suite installed. + + Option: + + -C + Don't containerize the command, run it directly in the current + namespaces + + -f + Infix image to test + + -q + Directory containing a topology.dot.in, suitable for consumption + by qeneth +EOF +} + +testdir=$(dirname $(readlink -f "$0")) +ixdir=$(readlink -f "$testdir/..") +envdir="$HOME/.infix-test-venv" +qeneth="$ixdir/qeneth/qeneth" + +# Global options +containerize=yes +while getopts "Cf:q:" opt; do + case ${opt} in + C) + containerize= + ;; + f) + imgfile="$OPTARG" + ;; + q) + qenethdir="$OPTARG" + ;; + esac +done + +if [ "$containerize" ]; then + if which podman >/dev/null; then + runner=podman + elif which docker >/dev/null; then + runner=docker + else + echo "Error: Neither podman nor docker is installed" + exit 1 + fi + + kvm= + if [ -c /dev/kvm ]; then + kvm="--device=/dev/kvm" + fi + + exec $runner run \ + --cap-add=NET_ADMIN \ + --device=/dev/net/tun \ + --interactive \ + --rm \ + --security-opt seccomp=unconfined \ + --sysctl net.ipv6.conf.all.disable_ipv6=0 \ + --tty \ + --volume "$ixdir":"$ixdir" \ + --workdir $(pwd) \ + $kvm \ + $INFIX_TEST \ + "$0" -C "$@" +fi + +shift $((OPTIND - 1)) + +if [ $# -lt 1 ]; then + usage && exit 1 +fi + +. "$envdir/bin/activate" +export PYTHONPATH="$testdir" + +extract_yang "$imgfile" +start_topology "$qenethdir" "$imgfile" +export INFAMY_ARGS + +exec "$@"