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 --- tests/common/__init__.py | 0 tests/common/accel_pppd_process.py | 86 ++++++++++++++++++++++++++++++++++++++ tests/common/config.py | 15 +++++++ tests/common/dhclient_process.py | 45 ++++++++++++++++++++ tests/common/netns.py | 29 +++++++++++++ tests/common/pppd_process.py | 43 +++++++++++++++++++ tests/common/process.py | 7 ++++ tests/common/veth.py | 84 +++++++++++++++++++++++++++++++++++++ tests/common/vlan.py | 13 ++++++ 9 files changed, 322 insertions(+) create mode 100644 tests/common/__init__.py create mode 100644 tests/common/accel_pppd_process.py create mode 100644 tests/common/config.py create mode 100644 tests/common/dhclient_process.py create mode 100644 tests/common/netns.py create mode 100644 tests/common/pppd_process.py create mode 100644 tests/common/process.py create mode 100644 tests/common/veth.py create mode 100644 tests/common/vlan.py (limited to 'tests/common') diff --git a/tests/common/__init__.py b/tests/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/common/accel_pppd_process.py b/tests/common/accel_pppd_process.py new file mode 100644 index 0000000..c2ee451 --- /dev/null +++ b/tests/common/accel_pppd_process.py @@ -0,0 +1,86 @@ +from subprocess import Popen, PIPE +from common import process +from threading import Thread +import time + + +def accel_pppd_thread_func(accel_pppd_control): + process = accel_pppd_control["process"] + print("accel_pppd_thread_func: before communicate") + (out, err) = process.communicate() + print( + "accel_pppd_thread_func: after communicate out=" + str(out) + " err=" + str(err) + ) + process.wait() + print("accel_pppd_thread_func: after wait") + + +def start(accel_pppd, args, accel_cmd, max_wait_time): + print("accel_pppd_start: begin") + accel_pppd_process = Popen([accel_pppd] + args, stdout=PIPE, stderr=PIPE) + accel_pppd_control = {"process": accel_pppd_process} + accel_pppd_thread = Thread( + target=accel_pppd_thread_func, + args=[accel_pppd_control], + ) + accel_pppd_thread.start() + + # wait until accel-pppd replies to 'show version' + # accel-pppd needs some time to be accessible + sleep_time = 0.0 + is_started = False + while sleep_time < max_wait_time: + if accel_pppd_process.poll() is not None: # process is terminated + print( + "accel_pppd_start: terminated during 'show version' polling in (sec): " + + str(sleep_time) + ) + is_started = False + break + (exit, out, err) = process.run([accel_cmd, "show version"]) + if exit != 0: # does not reply + time.sleep(0.1) + sleep_time += 0.1 + else: # replied + print("accel_pppd_start: 'show version' replied") + is_started = True + break + + return (is_started, accel_pppd_thread, accel_pppd_control) + + +def end(accel_pppd_thread, accel_pppd_control, accel_cmd, max_wait_time): + print("accel_pppd_end: begin") + if accel_pppd_control["process"].poll() is not None: # terminated + print("accel_pppd_end: already terminated. nothing to do") + accel_pppd_thread.join() + return + + process.run( + [accel_cmd, "shutdown hard"] + ) # send shutdown hard command (in coverage mode it helps saving coverage data) + print("accel_pppd_end: after shutdown hard") + + # wait until accel-pppd is finished + sleep_time = 0.0 + is_finished = False + while sleep_time < max_wait_time: + if accel_pppd_control["process"].poll() is None: # not terminated yet + time.sleep(0.01) + sleep_time += 0.01 + # print("accel_pppd_end: sleep 0.01") + else: + is_finished = True + print( + "accel_pppd_end: finished via shutdown hard in (sec): " + + str(sleep_time) + ) + break + + # accel-pppd is still alive. kill it + if not is_finished: + print("accel_pppd_end: kill process: " + str(accel_pppd_control["process"])) + accel_pppd_control["process"].kill() # kill -9 if 'shutdown hard' didn't help + + accel_pppd_thread.join() # wait until thread is finished + print("accel_pppd_end: end") diff --git a/tests/common/config.py b/tests/common/config.py new file mode 100644 index 0000000..94ddcd8 --- /dev/null +++ b/tests/common/config.py @@ -0,0 +1,15 @@ +import tempfile +import os + + +def make_tmp(content): + f = tempfile.NamedTemporaryFile(delete=False) + print("make_tmp filename: " + f.name) + f.write(bytes(content, "utf-8")) + f.close() + return f.name + + +def delete_tmp(filename): + print("delete_tmp filename: " + filename) + os.unlink(filename) diff --git a/tests/common/dhclient_process.py b/tests/common/dhclient_process.py new file mode 100644 index 0000000..aaea486 --- /dev/null +++ b/tests/common/dhclient_process.py @@ -0,0 +1,45 @@ +from subprocess import Popen, PIPE +from threading import Thread + + +def dhclient_thread_func(dhclient_control): + process = dhclient_control["process"] + print("dhclient_thread_func: before communicate") + (out, err) = process.communicate() + print( + "dhclient_thread_func: after communicate out=" + str(out) + " err=" + str(err) + ) + process.wait() + print("dhclient_thread_func: after wait") + + +def start(netns, dhclient, args): + print("dhclient_start: begin") + print("dhclient_start: args=" + str(args)) + dhclient_process = Popen( + ["ip", "netns", "exec", netns] + [dhclient] + args, stdout=PIPE, stderr=PIPE + ) + print("dhclient_start: dhclient_process=" + str(dhclient_process)) + dhclient_control = {"process": dhclient_process} + dhclient_thread = Thread( + target=dhclient_thread_func, + args=[dhclient_control], + ) + dhclient_thread.start() + + is_started = True if dhclient_process.poll() is None else False + + return (is_started, dhclient_thread, dhclient_control) + + +def end(dhclient_thread, dhclient_control): + print("dhclient_end: begin") + if dhclient_control["process"].poll() is not None: # already terminated + print("dhclient_end: already terminated. nothing to do") + dhclient_thread.join() + return + + print("dhclient_end: kill process: " + str(dhclient_control["process"])) + dhclient_control["process"].kill() # kill -9 + dhclient_thread.join() # wait until thread is finished + print("dhclient_end: end") diff --git a/tests/common/netns.py b/tests/common/netns.py new file mode 100644 index 0000000..9b0d729 --- /dev/null +++ b/tests/common/netns.py @@ -0,0 +1,29 @@ +from common import process + +# creates netns and returns netns name. if ok return 0 +def create(netns_name): + netns, out, err = process.run(["ip", "netns", "add", netns_name]) + print("netns.create: exit=%d out=%s err=%s" % (netns, out, err)) + + return netns + + +# deletes netns. if ok return 0 +def delete(netns_name): + netns, out, err = process.run(["ip", "netns", "delete", netns_name]) + print("netns.delete: exit=%d out=%s err=%s" % (netns, out, err)) + + return netns + + +# execute command in netns using process.run +# if netns_name is None, then execute in global rt +def exec(netns_name, command): + if netns_name is None: + exit, out, err = process.run(command) + else: + exit, out, err = process.run(["ip", "netns", "exec", netns_name] + command) + + print("netns.exec: netns=%s command=%s :: exit=%d out=%s err=%s" % (netns_name, str(command), exit, out, err)) + + return (exit, out, err) diff --git a/tests/common/pppd_process.py b/tests/common/pppd_process.py new file mode 100644 index 0000000..4584440 --- /dev/null +++ b/tests/common/pppd_process.py @@ -0,0 +1,43 @@ +from subprocess import Popen, PIPE +from threading import Thread + + +def pppd_thread_func(pppd_control): + process = pppd_control["process"] + print("pppd_thread_func: before communicate") + (out, err) = process.communicate() + print("pppd_thread_func: after communicate out=" + str(out) + " err=" + str(err)) + process.wait() + print("pppd_thread_func: after wait") + + +def start(netns, pppd, args): + print("pppd_start: begin") + print("pppd_start: args=" + str(args)) + pppd_process = Popen( + ["ip", "netns", "exec", netns] + [pppd] + args, stdout=PIPE, stderr=PIPE + ) + print("pppd_start: pppd_process=" + str(pppd_process)) + pppd_control = {"process": pppd_process} + pppd_thread = Thread( + target=pppd_thread_func, + args=[pppd_control], + ) + pppd_thread.start() + + is_started = True if pppd_process.poll() is None else False + + return (is_started, pppd_thread, pppd_control) + + +def end(pppd_thread, pppd_control): + print("pppd_end: begin") + if pppd_control["process"].poll() is not None: # already terminated + print("pppd_end: already terminated. nothing to do") + pppd_thread.join() + return + + print("pppd_end: kill process: " + str(pppd_control["process"])) + pppd_control["process"].kill() # kill -9 + pppd_thread.join() # wait until thread is finished + print("pppd_end: end") diff --git a/tests/common/process.py b/tests/common/process.py new file mode 100644 index 0000000..e0c6136 --- /dev/null +++ b/tests/common/process.py @@ -0,0 +1,7 @@ +from subprocess import Popen, PIPE + +def run(command): + process = Popen(command, stdout=PIPE, stderr=PIPE) + (out, err) = process.communicate() + exit_code = process.wait() + return (exit_code, out.decode("utf-8"), err.decode("utf-8")) \ No newline at end of file diff --git a/tests/common/veth.py b/tests/common/veth.py new file mode 100644 index 0000000..a31d245 --- /dev/null +++ b/tests/common/veth.py @@ -0,0 +1,84 @@ +from common import process, netns, vlan +import time +import math + +# creates veth pair. if ok returns 0 +def create_pair(name_a, name_b): + veth, out, err = process.run( + ["ip", "link", "add", name_a, "type", "veth", "peer", "name", name_b] + ) + print("veth.create: exit=%d out=%s err=%s" % (veth, out, err)) + + 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]) + print("veth.assign_netns: exit=%d out=%s err=%s" % (veth, out, err)) + + 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' +def create_veth_pair_netns(veth_pair_vlans_config): + + name = str(math.floor(time.time() * 1000) % 1000000) + netns_name = "N" + name + netns_status = netns.create(netns_name) + print("create_veth_pair_netns: netns_status=%d" % netns_status) + + veth_a = "A" + name + veth_b = "B" + name + pair_status = create_pair(veth_a, veth_b) + print("create_veth_pair_netns: pair_status=%d" % pair_status) + + up_interface(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) + + 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) + + 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) + + return {"netns": netns_name, "veth_a": veth_a, "veth_b": veth_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"]) + print("delete_veth_pair_netns: veth_status=%d" % veth_status) + + netns_status = netns.delete(veth_pair_netns["netns"]) + print("delete_veth_pair_netns: netns_status=%d" % netns_status) diff --git a/tests/common/vlan.py b/tests/common/vlan.py new file mode 100644 index 0000000..459efb1 --- /dev/null +++ b/tests/common/vlan.py @@ -0,0 +1,13 @@ +from common import netns + +# up interface. if netns is None, then up in global rt. if ok returns 0 +def create(parent_if, vlan, netns_name): + command = ( + "ip link add link %s name %s.%d type vlan id %d" + % (parent_if, parent_if, vlan, vlan) + ).split() + + vlan, out, err = netns.exec(netns_name, command) + print("vlan.create: exit=%d out=%s err=%s" % (vlan, out, err)) + + return vlan -- 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 'tests/common') diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index dfa637c..f9cf1b8 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 3e937f8..a230c7c 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 0000000..dbd052a --- /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 a31d245..c9d3006 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