diff --git a/debian-package/README.md b/debian-package/README.md new file mode 100644 index 0000000..9b94628 --- /dev/null +++ b/debian-package/README.md @@ -0,0 +1,23 @@ +# EdgeVPNio DEBIAN PACKAGE + +The Debian package installs EdgeVPNio (evio) as a systemd service and is supported in Ubuntu 18.04 and Raspberry Pi Raspbian OS. Use the following procedure to create a new installer package. +1. Clone the `tools` repo and use `tools/deb-pak` as your base directory. +2. Copy the `tincan` executable, and the contents of the `controller` folder into `edge-vpnio/opt/edge-vpnio`. +3. Copy `config.json`, the template or completed file, into `edge-vpnio/etc/opt/edge-vpnio`. +4. Execute `./deb-gen` to create the `edge-vpnio.deb` installer package. + +By default, the following files and directories are created: +1. `/opt/edge-vpnio/tincan` +2. `/opt/edge-vpnio/controller/` +3. `/etc/opt/edge-vpnio/config.json` +4. `/etc/systemd/system` +5. `/var/logs/edge-vpnio/tincan_log` +6. `/var/logs/edge-vpnio/ctrl.log` + +The installer has dependencies on, and will install `python3`, `python3-pip`, `iproute2`, `openvswitch-switch`, `bridge-utils`. +To install EdgeVPNio invoke `sudo apt install -y /edge-vpnio.deb`. +After installation but before starting evio, complete `config.json` by adding the XMPP credentials, setting the IP address, and applying other configurations as needed. +Then start evio using `sudo systemctl start evio`. +Additionally, use `systemctl` to start/stop/restart/status evio. + +EdgeVPNio is configured to be started automatically on reboot. diff --git a/debian-package/deb-gen b/debian-package/deb-gen new file mode 100755 index 0000000..c00a384 --- /dev/null +++ b/debian-package/deb-gen @@ -0,0 +1,18 @@ +#!/bin/sh + +cat < edge-vpnio/DEBIAN/control +Package: edge-vpnio +Name: EdgeVPNio +Version: 20.7 +Maintainer: +Architecture: $(dpkg --print-architecture) +Homepage: http://edgevpnio.github.io +Depends: python3, python3-dev, python3-pip, iproute2, openvswitch-switch, bridge-utils +Description: Virtual Overlay Networks. + EdgeVPN is an open-source user-centric software virtual network allowing end users to define and create their own virtual private networks (VPNs). EdgeVPNio virtual networks provide end-to-end tunneling of IP or Ethernet over Tincan links setup and managed through a control API to create various software-defined VPN overlays. +Tag: P2P Overlay Networking +! +cp edge-vpnio/opt/edge-vpnio/template-config.json edge-vpnio/etc/opt/edge-vpnio/config.json +dpkg-deb --build --root-owner-group edge-vpnio . + +rm edge-vpnio/DEBIAN/control edge-vpnio/etc/opt/edge-vpnio/config.json diff --git a/debian-package/edge-vpnio/DEBIAN/conffiles b/debian-package/edge-vpnio/DEBIAN/conffiles new file mode 100644 index 0000000..e067867 --- /dev/null +++ b/debian-package/edge-vpnio/DEBIAN/conffiles @@ -0,0 +1 @@ +/etc/opt/edge-vpnio/config.json diff --git a/debian-package/edge-vpnio/DEBIAN/postinst b/debian-package/edge-vpnio/DEBIAN/postinst new file mode 100755 index 0000000..624f9cf --- /dev/null +++ b/debian-package/edge-vpnio/DEBIAN/postinst @@ -0,0 +1,10 @@ +#!/bin/bash + +cd /opt/edge-vpnio +pip3 --no-cache-dir install virtualenv +virtualenv --python=python3.6 venv +source venv/bin/activate +pip3 --no-cache-dir install psutil sleekxmpp requests simplejson ryu +deactivate +systemctl daemon-reload +systemctl enable evio \ No newline at end of file diff --git a/debian-package/edge-vpnio/DEBIAN/prerm b/debian-package/edge-vpnio/DEBIAN/prerm new file mode 100755 index 0000000..2291a2e --- /dev/null +++ b/debian-package/edge-vpnio/DEBIAN/prerm @@ -0,0 +1,12 @@ +#!/bin/bash + +systemctl -q is-active evio && systemctl -q stop evio || true +systemctl -q is-enabled evio && systemctl -q disable evio || true +rm -rf /opt/edge-vpnio/venv +if [ -d /opt/edge-vpnio/controller ]; then + for dir in $(find /opt/edge-vpnio/controller -type d -name __pycache__); do + rm -rf "$dir" + done +fi +exit 0 + diff --git a/debian-package/edge-vpnio/etc/systemd/system/evio.service b/debian-package/edge-vpnio/etc/systemd/system/evio.service new file mode 100644 index 0000000..c866f32 --- /dev/null +++ b/debian-package/edge-vpnio/etc/systemd/system/evio.service @@ -0,0 +1,12 @@ +[Unit] +Description=EdgeVPNio service + +[Service] +Type=oneshot +ExecStart=/opt/edge-vpnio/evio-start +RemainAfterExit=true +Restart=no +TimeoutStopSec=30 + +[Install] +WantedBy=multi-user.target diff --git a/debian-package/edge-vpnio/opt/edge-vpnio/evio-start b/debian-package/edge-vpnio/opt/edge-vpnio/evio-start new file mode 100755 index 0000000..ca30665 --- /dev/null +++ b/debian-package/edge-vpnio/opt/edge-vpnio/evio-start @@ -0,0 +1,11 @@ +#! /bin/bash + +evio_start() { + cd /opt/edge-vpnio + ./tincan & + source venv/bin/activate + python Controller.py -c /etc/opt/edge-vpnio/config.json & +} + +evio_start +exit diff --git a/docker-image/99fixbadproxy b/docker-image/99fixbadproxy new file mode 100755 index 0000000..cafeaaf --- /dev/null +++ b/docker-image/99fixbadproxy @@ -0,0 +1,3 @@ +Acquire::http::Pipeline-Depth 0; +Acquire::http::No-Cache true; +Acquire::BrokenProxy true; diff --git a/docker-image/evio-base.Dockerfile b/docker-image/evio-base.Dockerfile new file mode 100755 index 0000000..6c5e87c --- /dev/null +++ b/docker-image/evio-base.Dockerfile @@ -0,0 +1,21 @@ +FROM solita/ubuntu-systemd:18.04 +WORKDIR /root/ +#fix for bad network URL proxy +COPY ./99fixbadproxy /etc/apt/apt.conf.d/99fixbadproxy +RUN apt-get update -y && apt-get install -y \ + psmisc \ + iputils-ping \ + nano \ + python3.6 \ + python3.6-dev \ + python3-pip \ + python3-venv \ + iproute2 \ + openvswitch-switch \ + bridge-utils \ + iperf \ + tcpdump + +COPY ./setup-prereqs.sh . +RUN chmod +x ./setup-prereqs.sh +RUN ./setup-prereqs.sh diff --git a/docker-image/evio-node.Dockerfile b/docker-image/evio-node.Dockerfile new file mode 100755 index 0000000..d28bea4 --- /dev/null +++ b/docker-image/evio-node.Dockerfile @@ -0,0 +1,6 @@ +FROM kcratie/evio-base:1.0 +WORKDIR /root/ +COPY ./edge-vpnio_20.7_amd64.deb . +RUN apt-get install -y ./edge-vpnio_20.7_amd64.deb && rm -rf /var/lib/apt/lists/* && apt-get autoclean + +CMD ["/sbin/init"] diff --git a/docker-image/setup-prereqs.sh b/docker-image/setup-prereqs.sh new file mode 100755 index 0000000..e87b74d --- /dev/null +++ b/docker-image/setup-prereqs.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +systemctl mask getty@tty1.service && \ +mkdir -p /opt/edge-vpnio && \ +cd /opt/edge-vpnio && \ +python3 -m venv venv && \ +source venv/bin/activate && \ +pip3 --no-cache-dir install psutil sleekxmpp requests simplejson ryu && \ +deactivate diff --git a/setup b/setup new file mode 100755 index 0000000..3a7c093 --- /dev/null +++ b/setup @@ -0,0 +1,141 @@ +#!/bin/bash + +CONTROLLER=https://github.com/EdgeVPNio/controller +TINCAN=https://github.com/EdgeVPNio/tincan +EXLIBS=https://github.com/ipop-project/3rd-Party-Libs.git +PYVER=3.6 +EVIOVER=_20.7_amd64 + +function install_build_tools +{ + sudo bash -c " + apt-get update -y && \ + apt-get install -y build-essential + " +} + +function pull_src +{ + git clone $CONTROLLER .. + git clone $TINCAN .. + wd=$(pwd) + cd ../evio/tincan/external + git clone -b ubuntu-x64 --single-branch https://github.com/ipop-project/3rd-Party-Libs.git + cd $wd +} + +function make_tincan +{ + wd=$(pwd) + cd ../evio/tincan/trunk/build + make clean; make + cd $wd +} + +function make_debpak +{ + cp -r ../controller/* debian-package/edge-vpnio/opt/edge-vpnio/ && \ + cp ../tincan/trunk/out/release/x86_64/tincan debian-package/edge-vpnio/opt/edge-vpnio/ && \ + chmod +x debian-package/edge-vpnio/opt/edge-vpnio/tincan && \ + ./deb-gen + +} + +function make_testbed_prereqs +{ + sudo bash -c " + apt-get update -y && \ + apt-get install -y openvswitch-switch \ + python$PYVER python3-pip python3-venv python3-dev \ + apt-transport-https \ + ca-certificates \ + curl git \ + software-properties-common && \ + + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - && \ + add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable\" && \ + apt-cache policy docker-ce && \ + apt-get install -y containerd.io \ + docker-ce-cli \ + docker-ce && \ + groupadd -f docker && \ + usermod -a -G docker $USER \ + " + echo "You must logout and relogin for docker group membership to take effect." +} + +function make_venv +{ + wd=$(pwd) + cd testbed && \ + python$PYVER -m venv venv && \ + source venv/bin/activate && \ + pip3 install simplejson + cd $wd +} + +function make_dkrimg +{ + rm -f docker-image/edge-vpnio_20.7_amd64.deb + mv debian-package/edge-vpnio_20.7_amd64.deb docker-image/ && \ + docker build -f ./docker-image/evio-base.Dockerfile -t kcratie/evio-base:1.0 ./docker-image && \ + docker build -f ./docker-image/evio-node.Dockerfile -t kcratie/evio-node:20.7 ./docker-image +} + +function install_openfire +{ + docker run --name openfire -d \ + -p 9090:9090 -p 5222:5222 \ + -p 5269:5269 -p 5223:5223 \ + -p 7443:7443 -p 7777:7777 \ + -p 7070:7070 -p 5229:5229 \ + -p 5275:5275 \ + edgevpnio/openfire_edgevpn_demo +} + +function install_netviz +{ + #Todo: visualizer not yet available + git clone https://github.com/edgevpnio/portal.git + cd portal/setup + ./setup.sh + chown -R $USER /users/$USER/ +} + +function do_full_setup +{ + install_build_tools + pull_src + make_tincan + make_debpak + make_dkrimg + make_testbed_prereqs + make_venv +} + +case $1 in + testbed) + make_testbed_prereqs + ;; + venv) + make_venv + ;; + img) + make_dkrimg + ;; + tincan) + make_tincan + ;; + src) + pull_src + ;; + xmpp) + install_openfire + ;; + all) + do_full_setup + ;; + *) + echo "no match on input -> $1" + ;; +esac diff --git a/testbed/testbed.py b/testbed/testbed.py new file mode 100644 index 0000000..dfc0df1 --- /dev/null +++ b/testbed/testbed.py @@ -0,0 +1,477 @@ +# pylint: disable=missing-docstring +try: + import simplejson as json +except ImportError: + import json +import os +import subprocess +import random +from distutils import spawn +import pickle +import argparse +import shutil +import time +from abc import ABCMeta, abstractmethod +import ipaddress + +class Testbed(): + __metaclass__ = ABCMeta + + LAUNCH_WAIT = 60 + BATCH_SZ = 5 + VIRT = NotImplemented + APT = spawn.find_executable("apt-get") + CONTAINER = NotImplemented + BF_VIRT_IMG = "kcratie/evio-node:20.7" + + def __init__(self, exp_dir=None): + parser = argparse.ArgumentParser(description="Configures and runs EdgeVPN Testbed") + parser.add_argument("--clean", action="store_true", default=False, dest="clean", + help="Removes all generated files and directories") + parser.add_argument("--configure", action="store_true", default=False, dest="configure", + help="Generates the config files and directories") + parser.add_argument("-v", action="store_true", default=False, dest="verbose", + help="Print testbed activity info") + parser.add_argument("--range", action="store", dest="range", + help="Specifies the testbed start and end range in format #,#") + parser.add_argument("--run", action="store_true", default=False, dest="run", + help="Runs the currently configured testbed") + parser.add_argument("--end", action="store_true", default=False, dest="end", + help="End the currently running testbed") + parser.add_argument("--info", action="store_true", default=False, dest="info", + help="Displays the current testbed configuration") + parser.add_argument("--setup", action="store_true", default=False, dest="setup", + help="Installs software requirements. Requires run as root.") + parser.add_argument("--pull", action="store_true", default=False, dest="pull", + help="Pulls the {} image from docker hub" + .format(Testbed.BF_VIRT_IMG)) + parser.add_argument("--lxd", action="store_true", default=False, dest="lxd", + help="Uses LXC containers") + parser.add_argument("--dkr", action="store_true", default=False, dest="dkr", + help="Use docker containers") + parser.add_argument("--ping", action="store", dest="ping", + help="Ping the specified address from each container") + parser.add_argument("--arp", action="store", dest="arp", + help="arPing the specified address from each container") + parser.add_argument("--evio", action="store", dest="evio", + help="Perform the specified service action: stop/start/restart") + parser.add_argument("--churn", action="store", dest="churn", + help="Restarts the specified amount of nodes in the overlay," + "one every interval") + parser.add_argument("--test", action="store", dest="test", + help="Performs latency and bandwidth test between random pairs of " + "nodes. Ex test=") + + self.args = parser.parse_args() + self.exp_dir = exp_dir + if not self.exp_dir: + self.exp_dir = os.path.abspath(".") + self.template_file = "{0}/template-config.json".format(self.exp_dir) + self.config_dir = "{0}/config".format(self.exp_dir) + self.log_dir = "{0}/log".format(self.exp_dir) + self.data_dir = "{0}/data".format(self.exp_dir) + self.cert_dir = "{0}/cert".format(self.exp_dir) + self.config_file_base = "{0}/config-".format(self.config_dir) + self.seq_file = "{0}/startup.list".format(self.exp_dir) + self.range_file = "{0}/range_file".format(self.exp_dir) + + if self.args.range: + rng = self.args.range.rsplit(",", 2) + self.range_end = int(rng[1]) + self.range_start = int(rng[0]) + elif not self.args.range and os.path.isfile("range_file"): + with open(self.range_file) as rng_fle: + rng = rng_fle.read().strip().rsplit(",", 2) + self.range_end = int(rng[1]) + self.range_start = int(rng[0]) + else: + raise RuntimeError("Range unspecified") + self.total_inst = self.range_end - self.range_start + self.seq_list = None #[range(self.range_end, self.range_start)] + self.load_seq_list() + + @classmethod + def runshell(cls, cmd): + """ Run a shell command. if fails, raise an exception. """ + if cmd[0] is None: + raise ValueError("No executable specified to run") + resp = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return resp + + @property + @abstractmethod + def gen_config(self, range_start, range_end): + pass + + @property + @abstractmethod + def start_instance(self, instance): + pass + + @property + @abstractmethod + def end(self): + pass + + def clean_config(self): + if os.path.isdir(self.config_dir): + shutil.rmtree(self.config_dir) + if self.args.verbose: + print("Removed dir {}".format(self.config_dir)) + if os.path.isfile(self.seq_file): + os.remove(self.seq_file) + if self.args.verbose: + print("Removed file {}".format(self.seq_file)) + + def make_clean(self): + self.clean_config() + if os.path.isdir(self.log_dir): + shutil.rmtree(self.log_dir) + if self.args.verbose: + print("Removed dir {}".format(self.log_dir)) + + def configure(self): + with open(self.range_file, "w") as rng_fle: + rng_fle.write(self.args.range) + self.gen_config(self.range_start, self.range_end) + self.save_seq_list() + + def save_seq_list(self): + with open(self.seq_file, "wb") as seq_fle: + pickle.dump(self.seq_list, seq_fle) + seq_fle.flush() + if self.args.verbose: + print("Instance sequence saved with {0} entries\n{1}" + .format(self.total_inst, self.seq_list)) + + def load_seq_list(self): + if os.path.isfile(self.seq_file): + with open(self.seq_file, "rb") as seq_fle: + self.seq_list = pickle.load(seq_fle) + if self.args.verbose: + print("Sequence list loaded from existing file - {0} entries\n{1}". + format(len(self.seq_list), self.seq_list)) + else: + self.seq_list = list(range(self.range_start, self.range_end)) + random.shuffle(self.seq_list) + + def start_range(self, num, wait): + cnt = 0 + sequence = self.seq_list[self.range_start-1:self.range_end] + for inst in sequence: + self.start_instance(inst) + cnt += 1 + if cnt % num == 0 and cnt < len(sequence): + #if self.args.verbose: + print("{0}/{1} container(s) instantiated".format(cnt, len(sequence))) + time.sleep(wait) + print("{0} container(s) instantiated".format(cnt)) + + def run(self): + self.start_range(Testbed.BATCH_SZ, Testbed.LAUNCH_WAIT) + + def display_current_config(self): + print("----Testbed Configuration----") + print("{0} instances range {1}-{2}".format(self.total_inst, self.range_start, + self.range_end)) + print("Config dir {0}".format(self.config_dir)) + print("Config base filename {0}".format(self.config_file_base)) + print("Log dir {0}".format(self.log_dir)) + print("Contianer image {0}".format(Testbed.BF_VIRT_IMG)) + print("".format()) + + def setup_system(self): + setup_cmds = [["./setup-system.sh"]] + for cmd_list in setup_cmds: + if self.args.verbose: + print(cmd_list) + resp = Testbed.runshell(cmd_list) + print(resp.stdout.decode("utf-8") if resp.returncode == 0 else + resp.stderr.decode("utf-8")) + + @abstractmethod + def run_container_cmd(self, cmd_line, instance_num): + pass + + def churn(self, param): + params = param.rsplit(",", 2) + iters = int(params[0]) + inval = int(params[1]) + self._churn(iters, inval) + + def _churn(self, churn_count=0, interval=30): + if churn_count == 0: + churn_count = self.total_inst + cnt = 0 + restarted_nds = set() + while cnt < churn_count: + inst = random.choice(range(self.range_start, self.range_end)) + print("Stopping node", inst) + self.run_container_cmd(["systemctl", "stop", "evio"], inst) + if self.args.verbose: + print("Waiting", interval, "seconds") + time.sleep(interval) + print("Resuming node", inst) + self.run_container_cmd(["systemctl", "start", "evio"], inst) + restarted_nds.add(inst) + cnt += 1 + if self.args.verbose: + print("Waiting", interval, "seconds") + time.sleep(interval) + if self.args.verbose: + print("{0} nodes restarted\n{1}".format(cnt, str(restarted_nds))) + + def run_test(self): + # test = None + # if self.args.test == "lui": + # test = TestLinkUtilization() + # test.create_input_files() + # if self.args.test == "lur": + # test = TestLinkUtilization() + # test.create_result_report() + print("Test case not implemented") + +class DockerTestbed(Testbed): + VIRT = spawn.find_executable("docker") + CONTAINER = "evio-dkr{0}" + + def __init__(self, exp_dir=None): + super().__init__(exp_dir=exp_dir) + self.network_name = "dkrnet" + + #def configure(self): + # super().configure() + # self.pull_image() + + def create_network(self): + #netid=docker network ls | grep dkrnet | awk 'BEGIN { FS=" "} {print $2}' + #docker network create dkrnet + pass + + def gen_config(self, range_start, range_end): + with open(self.template_file) as cfg_tmpl: + template = json.load(cfg_tmpl) + olid = template["CFx"].get("Overlays", None) + olid = olid[0] + node_id = template["CFx"].get("NodeId", "a000###feb6040628e5fb7e70b04f###") + node_name = template["OverlayVisualizer"].get("NodeName", "dkr###") + netwk = template["BridgeController"]["Overlays"][olid]["NetDevice"]["AppBridge"].get("NetworkAddress", "10.10.1.0/24") + netwk = ipaddress.IPv4Network(netwk) + for val in range(range_start, range_end): + rng_str = "{0:03}".format(val) + cfg_file = "{0}{1}.json".format(self.config_file_base, rng_str) + node_id = "{0}{1}{2}{1}{3}".format(node_id[:4], rng_str, node_id[7:29], node_id[32:]) + node_name = "{0}{1}".format(node_name[:3], rng_str) + node_ip = str(netwk[val]) + template["CFx"]["NodeId"] = node_id + template["OverlayVisualizer"]["NodeName"] = node_name + template["BridgeController"]["Overlays"][olid]["NetDevice"]["AppBridge"]["IP4"] = node_ip + template["BridgeController"]["Overlays"][olid]["NetDevice"]["AppBridge"]["PrefixLen"] = netwk.prefixlen + os.makedirs(self.config_dir, exist_ok=True) + with open(cfg_file, "w") as cfg_fle: + json.dump(template, cfg_fle, indent=2) + cfg_fle.flush() + if self.args.verbose: + print("{0} config file(s) generated".format(range_end-range_start)) + + def start_instance(self, instance): + instance = "{0:03}".format(instance) + container = DockerTestbed.CONTAINER.format(instance) + log_dir = "{0}/dkr{1}".format(self.log_dir, instance) + os.makedirs(log_dir, exist_ok=True) + + cfg_file = "{0}{1}.json".format(self.config_file_base, instance) + if not os.path.isfile(cfg_file): + self.gen_config(instance, instance+1) + + mount_cfg = "{0}:/etc/opt/edge-vpnio/config.json".format(cfg_file) + mount_log = "{0}/:/var/log/edge-vpnio/".format(log_dir) + #mount_data = "{0}/:/var/edge-vpnio/".format(self.data_dir) + mount_cert = "{0}/:/var/edge-vpnio/cert/".format(self.cert_dir) + args = ["--rm", "--privileged"] + opts = "-d" + img = Testbed.BF_VIRT_IMG + cmd = "/sbin/init" + cmd_list = [DockerTestbed.VIRT, "run", opts, "-v", mount_cfg, "-v", mount_log, "-v", mount_cert, + args[0], args[1], "--name", container, "--network", self.network_name, + img, cmd] + if self.args.verbose: + print(cmd_list) + resp = Testbed.runshell(cmd_list) + print(resp.stdout.decode("utf-8") if resp.returncode == 0 else resp.stderr.decode("utf-8")) + + def run_container_cmd(self, cmd_line, instance_num): + #report = dict(fail_count=0, fail_node=[]) + cmd_list = [DockerTestbed.VIRT, "exec", "-it"] + inst = "{0:03}".format(instance_num) + container = DockerTestbed.CONTAINER.format(inst) + cmd_list.append(container) + cmd_list += cmd_line + resp = Testbed.runshell(cmd_list) + if self.args.verbose: + print(cmd_list) + print(resp.stdout.decode("utf-8")) + #if resp.returncode != 0: + # report["fail_count"] += 1 + # report["fail_node"].append("node-{0}".format(inst)) + #rpt_msg = "{0}: {1}/{2} failed\n{3}".format(cmd_line, report["fail_count"], + # self.range_end - self.range_start, + # report["fail_node"]) + #print(rpt_msg) + + def run_cmd_on_range(self, cmd_line, delay=0): + report = dict(fail_count=0, fail_node=[]) + for inst in self.seq_list[self.range_start-1:self.range_end]: + cmd_list = [DockerTestbed.VIRT, "exec", "-it"] + inst = "{0:03}".format(inst) + container = DockerTestbed.CONTAINER.format(inst) + cmd_list.append(container) + cmd_list += cmd_line + resp = Testbed.runshell(cmd_list) + if self.args.verbose: + print(cmd_list) + print(resp.stdout.decode("utf-8")) + if resp.returncode != 0: + report["fail_count"] += 1 + report["fail_node"].append("node-{0}".format(inst)) + if delay > 0: + time.sleep(delay) + rpt_msg = "{0}: {1}/{2} failed\n{3}".format(cmd_line, report["fail_count"], + self.range_end - self.range_start, + report["fail_node"]) + print(rpt_msg) + + + def pull_image(self): + cmd_list = [DockerTestbed.VIRT, "pull", Testbed.BF_VIRT_IMG] + resp = Testbed.runshell(cmd_list) + if self.args.verbose: + print(resp) + + def stop_range(self): + cnt = 0 + cmd_list = [DockerTestbed.VIRT, "kill"] + sequence = self.seq_list[self.range_start-1:self.range_end] + for inst in sequence: + cnt += 1 + inst = "{0:03}".format(inst) + container = DockerTestbed.CONTAINER.format(inst) + cmd_list.append(container) + if self.args.verbose: + print(cmd_list) + resp = Testbed.runshell(cmd_list) + print(resp.stdout.decode("utf-8") if resp.returncode == 0 else + resp.stderr.decode("utf-8")) + print("{0} Docker container(s) terminated".format(cnt)) + + def end(self): + self.run_cmd_on_range(["systemctl", "stop", "evio"]) + self.stop_range() + + def run_ping(self, target_address): + report = dict(fail_count=0, fail_node=[]) + for inst in range(self.range_start, self.range_end): + cmd_list = [DockerTestbed.VIRT, "exec", "-it"] + inst = "{0:03}".format(inst) + container = DockerTestbed.CONTAINER.format(inst) + cmd_list.append(container) + cmd_list += ["ping", "-c1"] + cmd_list.append(target_address) + resp = Testbed.runshell(cmd_list) + if self.args.verbose: + print(cmd_list) + print("ping ", target_address, "\n", resp.stdout.decode("utf-8")) + if resp.returncode != 0: + report["fail_count"] += 1 + report["fail_node"].append("node-{0}".format(inst)) + rpt_msg = "ping {0}: {1}/{2} failed\n{3}".format(target_address, report["fail_count"], + self.range_end - self.range_start, + report["fail_node"]) + print(rpt_msg) + + def run_arp(self, target_address): + for inst in range(self.range_start, self.range_end): + cmd_list = [DockerTestbed.VIRT, "exec", "-it"] + inst = "{0:03}".format(inst) + container = DockerTestbed.CONTAINER.format(inst) + cmd_list.append(container) + cmd_list += ["arping", "-C1"] + cmd_list.append(target_address) + if self.args.verbose: + print(cmd_list) + resp = Testbed.runshell(cmd_list) + print(resp.stdout.decode("utf-8") if resp.returncode == 0 else + resp.stderr.decode("utf-8")) + + def run_svc_ctl(self, svc_ctl): + if svc_ctl == "stop": + self.run_cmd_on_range(["systemctl", "stop", "evio"]) + elif svc_ctl == "start": + self.run_cmd_on_range(["systemctl", "start", "evio"], 10) + elif svc_ctl == "restart": + self.run_cmd_on_range(["systemctl", "restart", "evio"], 1) + else: + print("Invalid service control specified, only accepts start/stop/restart") + +def main(): # pylint: disable=too-many-return-statements + exp = DockerTestbed() + + + if exp.args.run and exp.args.end: + print("Error! Both run and end were specified.") + return + + if exp.args.info: + exp.display_current_config() + return + + if exp.args.setup: + exp.setup_system() + return + + if exp.args.pull: + exp.pull_image() + return + + if exp.args.clean: + exp.make_clean() + return + + if exp.range_end - exp.range_start <= 0: + print("Invalid range, please fix RANGE_START={0} RANGE_END={1}". + format(exp.range_start, exp.range_end)) + return + + if exp.args.configure: + exp.configure() + + if exp.args.run: + exp.run() + return + + if exp.args.end: + exp.end() + return + + if exp.args.ping: + exp.run_ping(exp.args.ping) + return + + if exp.args.arp: + exp.run_arp(exp.args.arp) + return + + if exp.args.evio: + exp.run_svc_ctl(exp.args.evio) + return + + if exp.args.churn: + exp.churn(exp.args.churn) + return + + if exp.args.test: + exp.run_test() + return + +if __name__ == "__main__": + main() diff --git a/update-limits.sh b/update-limits.sh new file mode 100755 index 0000000..c1a8e2b --- /dev/null +++ b/update-limits.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +function do_update +{ + cp /etc/sysctl.conf /etc/sysctl.conf.`date +%F-%H%M%S` + echo "kernel.keys.maxkeys=2000" >> /etc/sysctl.conf + echo "fs.inotify.max_queued_events=1048576" >> /etc/sysctl.conf + echo "fs.inotify.max_user_instances=1048576" >> /etc/sysctl.conf + echo "fs.inotify.max_user_watches=1048576" >> /etc/sysctl.conf + echo "vm.max_map_count=262144" >> /etc/sysctl.conf + echo "net.ipv4.neigh.default.gc_thresh3=8192" >> /etc/sysctl.conf + + cp /etc/security/limits.conf /etc/security/limits.conf.`date +%F-%H%M%S` + echo "* soft nofile 1048576" >> /etc/security/limits.conf + echo "* hard nofile 1048576" >> /etc/security/limits.conf + echo "root soft nofile 1048576" >> /etc/security/limits.conf + echo "root hard nofile 1048576" >> /etc/security/limits.conf + echo "* soft memlock unlimited" >> /etc/security/limits.conf + echo "* hard memlock unlimited" >> /etc/security/limits.conf +} + +echo "This script enables runing lots of containers on a single host." +echo "Note it must be run as root, sudo is insufficient." +echo "Do you wish to proceed?" +select yn in "Yes" "No"; do + case $yn in + Yes ) do_update; break;; + No ) exit;; + esac +done +reboot \ No newline at end of file