Running an Experiment

A typical experiment involves setting the congestion control protocols for each raspberry pi, setting the bandwidth and latency parameters for the router, and running a packet capture on each sender and client to monitor the flow.

Python Orchestration

I created a python script that uses a json configuration to set up each raspberry pi. The script can be seen here.

This script reads a json configuration file, and executes the bash scripts associated with each section. Lines ending with an & are treated run in the background by spawning a thread. Each of these threads must be joined before moving on to the next section (except in the setup phase). The substring {T} is replaced with the time for each trial.

Each command is run on the given host using ‘ssh’. This requires ssh keys to be set up for each host.

sample_config.json (click to show)

{
    "name": "80m_bbr_tbf",
    "time": 60,
    "setup": {
        "local": {
            "cc": "bbr",
            "commands": [
                "sudo sh Configs/80m_tbf.sh"
            ]
        },
        "pi@churro1": {
            "cc": "bbr",
            "commands": [
                "sudo tcpdump 'tcp port 5201' -w pcap.pcap -s 96 &"
            ]
        },
        "pi@churro2": {
            "cc": "bbr",
            "commands": [
                "sudo tcpdump 'tcp port 5201' -w pcap.pcap -s 96 &"
            ]
        },
        "pi@churro3": {
            "cc": "bbr",
            "commands": [
                "sudo tcpdump 'tcp port 5201' -w pcap.pcap -s 96 &"
            ]
        },
        "pi@churro4": {
            "cc": "bbr",
            "commands": [
                "sudo tcpdump 'tcp port 5201' -w pcap.pcap -s 96 &"
            ]
        },
        "pi@tarta1": {
            "cc": "bbr",
            "commands": [
                "iperf3 -s &",
                "sudo tcpdump 'tcp port 5201' -w pcap.pcap -s 96 &"
            ]
        },
        "pi@tarta2": {
            "cc": "bbr",
            "commands": [
                "iperf3 -s &",
                "sudo tcpdump 'tcp port 5201' -w pcap.pcap -s 96 &"
            ]
        },
        "pi@tarta3": {
            "cc": "bbr",
            "commands": [
                "iperf3 -s &",
                "sudo tcpdump 'tcp port 5201' -w pcap.pcap -s 96 &"
            ]
        },
        "pi@tarta4": {
            "cc": "bbr",
            "commands": [
                "iperf3 -s &",
                "sudo tcpdump 'tcp port 5201' -w pcap.pcap -s 96 &"
            ]
        }
    },
    "run": {
        "local": {
            "commands": [
            ]
        },

        "pi@churro1": {
            "commands": [
                "iperf3 -c tarta1 -t {T} &"
            ]
        },
        "pi@churro2": {
            "commands": [
                "sleep 2; iperf3 -c tarta2 -t {T} &"
            ]
        },
        "pi@churro3": {
            "commands": [
                "sleep 4; iperf3 -c tarta3 -t {T} &"
            ]
        },
        "pi@churro4": {
            "commands": [
                "sleep 6; iperf3 -c tarta4 -t {T} &"
            ]
        },
        "pi@tarta1": {
            "commands": [
            ]
        },
        "pi@tarta2": {
            "commands": [
            ]
        },
        "pi@tarta3": {
            "commands": [
            ]
        },
        "pi@tarta4": {
            "commands": [
            ]
        }
    }, 
    "finish": {
        "local": {
            "commands": [
                "sudo sh 10mbps_enp3_off.sh",
                "sudo tc -s qdisc ls dev enp3s0"
            ]
        },
        "pi@churro1": {
            "commands": [
                "sudo killall iperf3",
                "sudo killall tcpdump"
            ]
        },
        "pi@churro2": {
            "commands": [
                "sudo killall iperf3",
                "sudo killall tcpdump"
            ]
        },
        "pi@churro3": {
            "commands": [
                "sudo killall iperf3",
                "sudo killall tcpdump"
            ]
        },
        "pi@churro4": {
            "commands": [
                "sudo killall iperf3",
                "sudo killall tcpdump"
            ]
        },
        "pi@tarta1": {
            "commands": [
                "sudo killall iperf3",
                "sudo killall tcpdump"
            ]
        },
        "pi@tarta2": {
            "commands": [
                "sudo killall iperf3",
                "sudo killall tcpdump"
            ]
        },
        "pi@tarta3": {
            "commands": [
                "sudo killall iperf3",
                "sudo killall tcpdump"
            ]
        },
        "pi@tarta4": {
            "commands": [
                "sudo killall iperf3",
                "sudo killall tcpdump"
            ]
        }
    }
}

start_trial.py (click to show)

#! /usr/bin/python3
import os
import time
import sys
import json
import threading
from typing import Dict

if (not (len(sys.argv) == 2 or len(sys.argv) == 3)):
    print(f"error: enter one or two args, you enetered {len(sys.argv)} args")
    exit()


def c(host, cmd):
    """
    turn into remote version of command
    """
    cmd = cmd.replace("&", "")
    cmd = cmd.replace("{T}", f"{trial_time}")
    if (host != 'local'):
        cmd_clean = f"ssh {host} "
        cmd_clean += "\"" + cmd + "\""
        return cmd_clean
    return cmd


def exec_cmd(host, cmd):
    new_thread = "&" in cmd
    cmd = c(host, cmd)
    print("command: ", cmd)
    if (new_thread):
        t = threading.Thread(target=lambda : os.system(cmd))
        t.name = f"{host} $ {cmd}"
        t.start()
        return t
    else:
        os.system(cmd)
        return False


def set_cc(host, cc):
    cmd = \
f"""\
sudo sysctl net.core.default_qdisc=fq;
sudo sysctl net.ipv4.tcp_congestion_control={cc};
sudo sysctl net.ipv4.tcp_congestion_control;
"""
    cmd = c(host, cmd);
    print(cmd)
    os.system(cmd)
    pass

config: Dict = json.loads(open(sys.argv[1], 'r').read())
name = config["name"]
trial_time = config["time"]
if (len(sys.argv) > 2):
    name = sys.argv[2]
print(config)

for host, conf in config["setup"].items():
    print(f"Configuring: {host}\n")
    set_cc(host, conf["cc"])
    for command in conf["commands"]:
        exec_cmd(host, command)

# Sleep to allow threads to start process up etc.
time.sleep(3)

run_handles = []
for host, conf in config["run"].items():
    print(f"Running : {host}\n")
    for command in conf["commands"]:
        join_handle = exec_cmd(host, command)
        run_handles.append(join_handle)
# Block and wait for all tasks started in the run phase
for handle in run_handles:
    handle.join()

for host, conf in config["finish"].items():
    print(f"Running : {host}\n")
    for command in conf["commands"]:
        exec_cmd(host, command)

# mkdir if it doesn't exist in results
if (not os.path.isdir(f"Results/{name}/")):
    os.mkdir(f"Results/{name}/")

for host, conf in config["run"].items():
    cc = config["setup"][host]["cc"]
    cmd = f"scp {host}:~/pcap.pcap Results/{name}/{cc}_{host}.pcap"
    print("running: ", cmd)
    os.system(cmd)

os.system("sh ./10mbps_enp3_off.sh")