From c92ff6266b18a9655edef231391739f0479dfb3a Mon Sep 17 00:00:00 2001 From: "Sergey V. Lobanov" Date: Sun, 4 Sep 2022 18:49:42 +0300 Subject: add tests and ci workflow for running tests This commit adds tests (using python3 pytest framework): 1. Test basic accel-cmd commands (show version, show stat, etc) 2. Test ipoe shared session up (dhcpv4) without radius 3. Test pppoe discovery (without PADO delay) 4. Test pppoe discovery (without PADO delay) 5. Test pppoe session up (ipv4) without radius 6. Test vlan creation using vlan-mon (pppoe) These tests require external utils. Please read tests/README.md how to setup environment, how to run the tests and how to generate coverage report Also, run-tests.yml contains step-by-step instruction how to run the tests Signed-off-by: Sergey V. Lobanov --- .github/workflows/run-tests.yml | 130 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 .github/workflows/run-tests.yml (limited to '.github/workflows') diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 00000000..dfa637c2 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,130 @@ +name: Run tests + +on: + workflow_dispatch: + pull_request: + push: + branches: + - master + +jobs: + Build-and-Test: + #if: ${{ false }} # disable for now + strategy: + fail-fast: false + matrix: + distro: ["ubuntu-22.04", "ubuntu-20.04"] + + runs-on: ${{ matrix.distro }} + steps: + - name: Install build tools (using apt) + run: > + sudo apt update && + sudo apt -y install git build-essential cmake gcc linux-headers-`uname -r` + libpcre3-dev libssl-dev liblua5.1-0-dev kmod python3-pip + iproute2 ppp pppoe isc-dhcp-client + + - name: Install testing tools (using pip) + run: > + sudo pip3 install pytest pytest-dependency gcovr + + - name: Check out repository code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: mkdir build + run: mkdir build + + - name: cmake (with coverage) + working-directory: ./build + run: > + cmake -DBUILD_IPOE_DRIVER=TRUE -DBUILD_VLAN_MON_DRIVER=TRUE -DCMAKE_INSTALL_PREFIX=/usr + -DKDIR=/usr/src/linux-headers-`uname -r` + -DLUA=TRUE -DSHAPER=TRUE -DRADIUS=TRUE .. + + - name: make && make install + working-directory: ./build + run: make && sudo make install + + - name: Insert and check kernel modules (ipoe and vlan-mon) + # if: ${{ false }} + run: | + sudo insmod build/drivers/vlan_mon/driver/vlan_mon.ko + sudo insmod build/drivers/ipoe/driver/ipoe.ko + lsmod | grep ipoe + lsmod | grep vlan_mon + + - name: Run tests + working-directory: ./tests + run: sudo python3 -m pytest -Wall -v + + Build-and-Test-With-Coverage: + #if: ${{ false }} # disable for now + strategy: + fail-fast: false + matrix: + distro: ["ubuntu-22.04", "ubuntu-20.04"] + + runs-on: ${{ matrix.distro }} + steps: + - name: Install build tools (using apt) + run: > + sudo apt update && + sudo apt -y install git build-essential cmake gcc linux-headers-`uname -r` + libpcre3-dev libssl-dev liblua5.1-0-dev kmod python3-pip + iproute2 ppp pppoe isc-dhcp-client + + - name: Install testing tools (using pip) + run: > + sudo pip3 install pytest pytest-dependency gcovr + + - name: Check out repository code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: mkdir build + run: mkdir build + + - name: cmake (with coverage) + working-directory: ./build + run: > + cmake -DBUILD_IPOE_DRIVER=TRUE -DBUILD_VLAN_MON_DRIVER=TRUE -DCMAKE_INSTALL_PREFIX=/usr + -DKDIR=/usr/src/linux-headers-`uname -r` + -DLUA=TRUE -DSHAPER=TRUE -DRADIUS=TRUE + -DCMAKE_C_FLAGS="--coverage -O0" .. + + - name: make && make install + working-directory: ./build + run: make && sudo make install + + - name: Insert and check kernel modules (ipoe and vlan-mon) + # if: ${{ false }} + run: | + sudo insmod build/drivers/vlan_mon/driver/vlan_mon.ko + sudo insmod build/drivers/ipoe/driver/ipoe.ko + lsmod | grep ipoe + lsmod | grep vlan_mon + + - name: Run tests (for coverage report) (fail is ok) + working-directory: ./tests + run: sudo python3 -m pytest -Wall -v || exit 0 + + - name: Generate coverage reports (default(txt), csv, html) + run: | + mkdir -p tests/report + gcovr --config=tests/gcovr.conf --output=tests/report/accel-ppp.txt + gcovr --config=tests/gcovr.conf --csv --output=tests/report/accel-ppp.csv + gcovr --config=tests/gcovr.conf --html --html-details --output=tests/report/accel-ppp.html + + - name: Show default coverage report + run: cat tests/report/accel-ppp.txt + + - name: Upload coverage report + # if: ${{ false }} + uses: actions/upload-artifact@v3 + with: + name: coverage-report-on-${{ matrix.distro }} + path: tests/report/ + if-no-files-found: error -- cgit v1.2.3 From 127b1de95923fccdfdc892c20f931d364e099f4b Mon Sep 17 00:00:00 2001 From: "Sergey V. Lobanov" Date: Mon, 5 Sep 2022 23:02:15 +0300 Subject: tests ci: add testing in Qemu (Ubuntu 18.04, 20.04, 22.04, Debian 10,11,12) --- .github/workflows/run-tests.yml | 179 ++++++++++++++++++++++++++++++- tests/accel-pppd/test_vlan_mon_driver.py | 1 + tests/common/iface.py | 23 ++++ tests/common/veth.py | 55 +++++----- 4 files changed, 228 insertions(+), 30 deletions(-) create mode 100644 tests/common/iface.py (limited to '.github/workflows') diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index dfa637c2..f9cf1b84 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,7 +8,180 @@ on: - master jobs: - Build-and-Test: + Test-in-Qemu: + #if: ${{ false }} # disable for now + runs-on: ubuntu-latest + name: Test in Qemu (${{ matrix.distro }}) + strategy: + fail-fast: false + matrix: + include: + - distro: Ubuntu-22.04 + image: https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img + untar: false + format: qcow2 + + - distro: Ubuntu-20.04 + image: https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img + untar: false + format: qcow2 + + - distro: Ubuntu-18.04 + image: https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img + untar: false + format: qcow2 + + - distro: Debian12 + image: https://cloud.debian.org/images/cloud/bookworm/daily/latest/debian-12-generic-amd64-daily.tar.xz + untar: true + format: raw + + - distro: Debian11 + image: https://cdimage.debian.org/images/cloud/bullseye/latest/debian-11-generic-amd64.tar.xz + untar: true + format: raw + + - distro: Debian10 + image: https://cloud.debian.org/images/cloud/buster/latest/debian-10-generic-amd64.tar.xz + untar: true + format: raw + + steps: + - name: Check out repository code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + path: "accel-ppp" + - name: Install qemu and required tools + run: > + sudo apt update && + sudo apt -y install qemu-system-x86 qemu-utils cloud-image-utils cpu-checker cloud-image-utils wget openssh-client screen + - name: Check kvm support (fail is ok) + run: sudo kvm-ok || exit 0 + - name: Prepare cloud-init image disk + run: | + ssh-keygen -t ed25519 -q -N "" -f ssh-key + echo "instance-id: $(uuidgen || echo i-abcdefg)" > init-meta + echo "#cloud-config" > init-data + echo "package_update: true" >> init-data + echo "package_upgrade: true" >> init-data + echo "package_reboot_if_required: false" >> init-data + echo "users:" >> init-data + echo " - default" >> init-data + echo " - name: user" >> init-data + echo " shell: /bin/bash" >> init-data + echo " sudo: ALL=(ALL) NOPASSWD:ALL" >> init-data + echo " ssh_authorized_keys:" >> init-data + echo " - "`cat ssh-key.pub` >> init-data + echo "power_state:">> init-data + echo " mode: poweroff">> init-data + cat init-data + cloud-localds init.img init-data init-meta + - name: Download, unpack and resize target OS cloud image + if: ${{ matrix.untar }} + run: | + wget -nv ${{ matrix.image }} + mkdir img + tar -xf *.tar.xz -C img + qemu-img resize -f ${{ matrix.format }} img/`ls -1 img` +2G + - name: Download and resize target OS cloud image + if: ${{ !matrix.untar }} + run: | + mkdir img + wget -nv ${{ matrix.image }} -O img/image + qemu-img resize -f ${{ matrix.format }} img/`ls -1 img` +2G + - name: Run target OS first time (for cloud-init actions) + run: sudo qemu-system-x86_64 -m 4096 -nographic -drive format=${{ matrix.format }},file=img/`ls -1 img` -drive format=raw,file=init.img + - name: Run target OS + run: sudo screen -dmS qemu qemu-system-x86_64 -net nic -net user,hostfwd=tcp::2222-:22 -m 4096 -nographic -drive format=${{ matrix.format }},file=img/`ls -1 img` + - name: Check that target OS is running + run: | + sleep 1 + sudo screen -ls + - name: Wait for ssh connection + timeout-minutes: 30 + run: > + while ! ssh -o StrictHostKeyChecking=accept-new -p2222 -o ConnectTimeout=5 -i ssh-key user@localhost "exit 0"; + do + echo "Trying to establish ssh connection"; + sleep 5; + done; + cat ~/.ssh/known_hosts + - name: Display free space, current dir, kernel version and test sudo + run: | + ssh -i ssh-key -p2222 user@localhost "df -h" + ssh -i ssh-key -p2222 user@localhost "pwd" + ssh -i ssh-key -p2222 user@localhost "uname -a" + ssh -i ssh-key -p2222 user@localhost "sudo cat /etc/passwd" + - name: Install build tools (on target OS) + run: > + ssh -i ssh-key -p2222 user@localhost "sudo apt -y install + git build-essential cmake gcc linux-headers-\`uname -r\` + libpcre3-dev libssl-dev liblua5.1-0-dev kmod python3-pip + iproute2 ppp pppoe isc-dhcp-client timelimit && + sudo pip3 install pytest pytest-dependency gcovr" + - name: Copy source code to target OS + run: | + tar -Jcf accel-ppp.tar.xz accel-ppp + scp -i ssh-key -P2222 accel-ppp.tar.xz user@localhost: + ssh -i ssh-key -p2222 user@localhost "tar -xf accel-ppp.tar.xz" + - name: Build accel-ppp + run: > + ssh -i ssh-key -p2222 user@localhost "cd accel-ppp && + mkdir build && cd build && + cmake -DBUILD_IPOE_DRIVER=TRUE -DBUILD_VLAN_MON_DRIVER=TRUE -DCMAKE_INSTALL_PREFIX=/usr + -DKDIR=/usr/src/linux-headers-\`uname -r\` + -DLUA=TRUE -DSHAPER=TRUE -DRADIUS=TRUE .. && + make && sudo make install" + + - name: Run tests (not related to ipoe and vlan_mon drivers) + timeout-minutes: 5 + run: > + ssh -i ssh-key -p2222 user@localhost "cd accel-ppp/tests && + sudo python3 -m pytest -Wall -v -m \"not ipoe_driver and not vlan_mon_driver\"" + - name: Display processes and dmesg after tests + if: ${{ always() }} + run: > + ssh -i ssh-key -p2222 user@localhost "ps aux | grep accel- && + sudo dmesg" + + - name: Insert ipoe kernel module + run: > + ssh -i ssh-key -p2222 user@localhost "cd accel-ppp && + sudo insmod build/drivers/ipoe/driver/ipoe.ko && + lsmod | grep ipoe " + + - name: Run tests (not related to vlan_mon drivers) + timeout-minutes: 5 + if: ${{ always() }} + run: > + ssh -i ssh-key -p2222 user@localhost "cd accel-ppp/tests && + sudo python3 -m pytest -Wall -v -m \"not vlan_mon_driver\"" + - name: Display processes and dmesg after tests + if: ${{ always() }} + run: > + ssh -i ssh-key -p2222 user@localhost "ps aux | grep accel- && + sudo dmesg" + + - name: Insert vlan_mon kernel module + run: > + ssh -i ssh-key -p2222 user@localhost "cd accel-ppp && + sudo insmod build/drivers/vlan_mon/driver/vlan_mon.ko && + lsmod | grep vlan_mon" + + - name: Run tests (all) + timeout-minutes: 5 + if: ${{ always() }} + run: > + ssh -i ssh-key -p2222 user@localhost "cd accel-ppp/tests && + sudo python3 -m pytest -Wall -v" + - name: Display processes and dmesg after tests + if: ${{ always() }} + run: > + ssh -i ssh-key -p2222 user@localhost "ps aux | grep accel- && + sudo dmesg" + + Test-in-GH: #if: ${{ false }} # disable for now strategy: fail-fast: false @@ -56,10 +229,11 @@ jobs: lsmod | grep vlan_mon - name: Run tests + timeout-minutes: 5 working-directory: ./tests run: sudo python3 -m pytest -Wall -v - Build-and-Test-With-Coverage: + Test-in-GH-Coverage: #if: ${{ false }} # disable for now strategy: fail-fast: false @@ -108,6 +282,7 @@ jobs: lsmod | grep vlan_mon - name: Run tests (for coverage report) (fail is ok) + timeout-minutes: 5 working-directory: ./tests run: sudo python3 -m pytest -Wall -v || exit 0 diff --git a/tests/accel-pppd/test_vlan_mon_driver.py b/tests/accel-pppd/test_vlan_mon_driver.py index 3e937f83..a230c7c8 100644 --- a/tests/accel-pppd/test_vlan_mon_driver.py +++ b/tests/accel-pppd/test_vlan_mon_driver.py @@ -3,5 +3,6 @@ import os # test that vlan_mon kernel module is loaded @pytest.mark.dependency(name = 'vlan_mon_driver_loaded', scope = 'session') +@pytest.mark.vlan_mon_driver def test_vlan_mon_kernel_module_loaded(): assert os.path.isdir("/sys/module/vlan_mon") \ No newline at end of file diff --git a/tests/common/iface.py b/tests/common/iface.py new file mode 100644 index 00000000..dbd052ab --- /dev/null +++ b/tests/common/iface.py @@ -0,0 +1,23 @@ +from common import netns + +# up interface. if netns is None, then up in global rt. if ok returns 0 +def up(iface, netns_name): + command = ["ip", "link", "set", iface, "up"] + exit, out, err = netns.exec(netns_name, command) + print( + "iface.up: iface=%s netns=%s exit=%d out=%s err=%s" + % (iface, netns_name, exit, out, err) + ) + + return exit + + +# delete interface. if netns is None, then up in global rt. if ok returns 0 +def delete(iface, netns_name): + exit, out, err = netns.exec(netns_name, ["ip", "link", "delete", iface]) + print( + "iface.delete: iface=%s netns=%s exit=%d out=%s err=%s" + % (iface, netns_name, exit, out, err) + ) + + return exit diff --git a/tests/common/veth.py b/tests/common/veth.py index a31d2453..c9d3006c 100644 --- a/tests/common/veth.py +++ b/tests/common/veth.py @@ -1,4 +1,4 @@ -from common import process, netns, vlan +from common import process, netns, vlan, iface import time import math @@ -12,14 +12,6 @@ def create_pair(name_a, name_b): return veth -# deletes veth pair. if ok returns 0 -def delete_veth(name_a): - veth, out, err = process.run(["ip", "link", "delete", name_a]) - print("veth.delete: exit=%d out=%s err=%s" % (veth, out, err)) - - return veth - - # put veth to netns. if ok returns 0 def assign_netns(veth, netns): veth, out, err = process.run(["ip", "link", "set", veth, "netns", netns]) @@ -28,18 +20,6 @@ def assign_netns(veth, netns): return veth -# up interface. if netns is None, then up in global rt. if ok returns 0 -def up_interface(iface, netns_name): - command = ["ip", "link", "set", iface, "up"] - exit, out, err = netns.exec(netns_name, command) - print( - "veth.up_interface: iface=%s netns=%s exit=%d out=%s err=%s" - % (iface, str(netns_name), exit, out, err) - ) - - return exit - - # creates netns, creates veth pair and place second link to netns # creates vlans over veth interfaces according to veth_pair_vlans_config # return dict with 'netns', 'veth_a', 'veth_b' @@ -55,30 +35,49 @@ def create_veth_pair_netns(veth_pair_vlans_config): pair_status = create_pair(veth_a, veth_b) print("create_veth_pair_netns: pair_status=%d" % pair_status) - up_interface(veth_a, None) + iface.up(veth_a, None) assign_status = assign_netns(veth_b, netns_name) print("create_veth_pair_netns: assign_status=%d" % assign_status) - up_interface(veth_b, netns_name) + iface.up(veth_b, netns_name) vlans_a = veth_pair_vlans_config["vlans_a"] for vlan_num in vlans_a: vlan.create(veth_a, vlan_num, None) - up_interface(veth_a + "." + str(vlan_num), None) + iface.up(veth_a + "." + str(vlan_num), None) vlans_b = veth_pair_vlans_config["vlans_b"] for vlan_num in vlans_b: vlan.create(veth_b, vlan_num, netns_name) - up_interface(veth_b + "." + str(vlan_num), netns_name) + iface.up(veth_b + "." + str(vlan_num), netns_name) - return {"netns": netns_name, "veth_a": veth_a, "veth_b": veth_b} + return { + "netns": netns_name, + "veth_a": veth_a, + "veth_b": veth_b, + "vlans_a": vlans_a, + "vlans_b": vlans_b, + } # deletes veth pair and netns created by create_veth_pair_netns def delete_veth_pair_netns(veth_pair_netns): - veth_status = delete_veth(veth_pair_netns["veth_a"]) + + vlans_a = veth_pair_netns["vlans_a"] + veth_a = veth_pair_netns["veth_a"] + vlans_b = veth_pair_netns["vlans_b"] + veth_b = veth_pair_netns["veth_b"] + netns_name = veth_pair_netns["netns"] + + # remove vlans on top of veth interface + for vlan_num in vlans_a: + iface.delete(veth_a + "." + str(vlan_num), None) + for vlan_num in vlans_b: + iface.delete(veth_b + "." + str(vlan_num), netns_name) + + veth_status = iface.delete(veth_a, None) print("delete_veth_pair_netns: veth_status=%d" % veth_status) - netns_status = netns.delete(veth_pair_netns["netns"]) + netns_status = netns.delete(netns_name) print("delete_veth_pair_netns: netns_status=%d" % netns_status) -- cgit v1.2.3