functioning automatic testing

This commit is contained in:
Jake Hillion 2020-11-05 17:25:23 +00:00
parent c5513d4aba
commit 165bb130d6
4 changed files with 411 additions and 76 deletions

View File

@ -1,5 +1,27 @@
{ {
"cells": [ "cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Project Evaluation\n",
"\n",
"This file interfaces with a Proxmox server to automatically generate VM structures and graphs for testing the\n",
"success criteria of my project."
]
},
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Setup\n",
"This section sets up the required variables for the Proxmox server."
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
@ -8,6 +30,7 @@
"source": [ "source": [
"import os\n", "import os\n",
"import ipaddress\n", "import ipaddress\n",
"import threading\n",
"\n", "\n",
"import runners\n", "import runners\n",
"from structure import Bridge\n", "from structure import Bridge\n",
@ -18,6 +41,18 @@
"%dotenv" "%dotenv"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Testing\n",
"This section gathers the required data from the different structures for later graphs."
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
@ -41,7 +76,6 @@
" internet_bridge=os.getenv('INTERNET_BRIDGE'),\n", " internet_bridge=os.getenv('INTERNET_BRIDGE'),\n",
"\n", "\n",
" management_bridge=os.getenv('MANAGEMENT_BRIDGE'),\n", " management_bridge=os.getenv('MANAGEMENT_BRIDGE'),\n",
" management_gateway=ipaddress.ip_address(os.getenv('MANAGEMENT_GATEWAY')),\n",
" management_initial_ip=ipaddress.ip_address(os.getenv('MANAGEMENT_INITIAL_IP')),\n", " management_initial_ip=ipaddress.ip_address(os.getenv('MANAGEMENT_INITIAL_IP')),\n",
")\n", ")\n",
"\n", "\n",
@ -49,7 +83,10 @@
" 'access_key': os.getenv('S3_ACCESS_KEY'),\n", " 'access_key': os.getenv('S3_ACCESS_KEY'),\n",
" 'secret_key': os.getenv('S3_SECRET_KEY'),\n", " 'secret_key': os.getenv('S3_SECRET_KEY'),\n",
" 'branch': os.getenv('TARGET_BRANCH'),\n", " 'branch': os.getenv('TARGET_BRANCH'),\n",
"}" "}\n",
"\n",
"directionInbound = {}\n",
"directionOutbound = {}"
] ]
}, },
{ {
@ -64,8 +101,49 @@
"source": [ "source": [
"rp = RemotePortal([Interface(IpMethod.Auto4)], setup_params=setup_params)\n", "rp = RemotePortal([Interface(IpMethod.Auto4)], setup_params=setup_params)\n",
"lp = LocalPortal([\n", "lp = LocalPortal([\n",
" Interface(IpMethod.Auto4, limit=1),\n", " Interface(IpMethod.Auto4),\n",
" Interface(IpMethod.Auto4, limit=1),\n", "], None, setup_params=setup_params)\n",
"\n",
"rp.set_local_portal(lp)\n",
"lp.set_remote_portal(rp)\n",
"\n",
"top_level_bridge = Bridge(*[\n",
" rp.get_interfaces()[0],\n",
" lp.get_interfaces()[0],\n",
"])\n",
"\n",
"try:\n",
" runner.build(top_level_bridge)\n",
"\n",
" lp.get_interfaces()[0].set_rate(1)\n",
" lp.speedtest_server()\n",
" directionInbound['One1MBNotProxied'] = rp.speedtest_client(lp.get_interfaces()[0].get_address())\n",
" rp.speedtest_server()\n",
" directionOutbound['One1MBNotProxied'] = lp.speedtest_client(rp.get_interfaces()[0].get_address())\n",
"\n",
" lp.get_interfaces()[0].set_rate(2)\n",
" lp.speedtest_server()\n",
" directionInbound['One2MBNotProxied'] = rp.speedtest_client(lp.get_interfaces()[0].get_address())\n",
" rp.speedtest_server()\n",
" directionOutbound['One2MBNotProxied'] = lp.speedtest_client(rp.get_interfaces()[0].get_address())\n",
"finally:\n",
" runner.teardown()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"rp = RemotePortal([Interface(IpMethod.Auto4)], setup_params=setup_params)\n",
"lp = LocalPortal([\n",
" Interface(IpMethod.Auto4),\n",
" Interface(IpMethod.Auto4),\n",
"], None, setup_params=setup_params)\n", "], None, setup_params=setup_params)\n",
"\n", "\n",
"rp.set_local_portal(lp)\n", "rp.set_local_portal(lp)\n",
@ -75,12 +153,138 @@
" rp.get_interfaces()[0],\n", " rp.get_interfaces()[0],\n",
" *lp.get_interfaces()[0:2],\n", " *lp.get_interfaces()[0:2],\n",
"])\n", "])\n",
"\n",
"try:\n",
" runner.build(top_level_bridge)\n", " runner.build(top_level_bridge)\n",
"\n", "\n",
"# Clean up\n", " lp.get_interfaces()[0].set_rate(1)\n",
" lp.get_interfaces()[1].set_rate(1)\n",
"\n",
" lp.speedtest_server()\n",
" directionInbound['Two1MBProxied'] = rp.speedtest_client('172.19.152.3')\n",
" rp.speedtest_server()\n",
" directionOutbound['Two1MBProxied'] = lp.speedtest_client('172.19.152.2')\n",
"\n",
" lp.get_interfaces()[0].set_rate(2)\n",
" lp.get_interfaces()[1].set_rate(2)\n",
"\n",
" lp.speedtest_server()\n",
" directionInbound['Two2MBProxied'] = rp.speedtest_client('172.19.152.3')\n",
" rp.speedtest_server()\n",
" directionOutbound['Two2MBProxied'] = lp.speedtest_client('172.19.152.2')\n",
"\n",
" lp.get_interfaces()[0].set_rate(1)\n",
" lp.get_interfaces()[1].set_rate(2)\n",
"\n",
" lp.speedtest_server()\n",
" directionInbound['One1MBOne2MBProxied'] = rp.speedtest_client('172.19.152.3')\n",
" rp.speedtest_server()\n",
" directionOutbound['One1MBOne2MBProxied'] = lp.speedtest_client('172.19.152.2')\n",
"\n",
" lp.get_interfaces()[0].set_rate(2)\n",
" lp.get_interfaces()[1].set_rate(2)\n",
"\n",
" lp.speedtest_server()\n",
" threading.Timer(5+15, lambda: lp.get_interfaces()[1].set_rate(1)).start()\n",
" threading.Timer(5+30, lambda: lp.get_interfaces()[1].set_rate(2)).start()\n",
"\n",
" directionInbound['One2MBOneYMBProxiedSlow15Return30'] = rp.speedtest_client('172.19.152.3', time=60)\n",
" rp.speedtest_server()\n",
" directionOutbound['One2MBOneYMBProxiedSlow15Return30'] = lp.speedtest_client('172.19.152.2', time=60)\n",
"finally:\n",
" runner.teardown()" " runner.teardown()"
] ]
}, },
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"rp = RemotePortal([Interface(IpMethod.Auto4)], setup_params=setup_params)\n",
"lp = LocalPortal([\n",
" Interface(IpMethod.Auto4),\n",
" Interface(IpMethod.Auto4),\n",
" Interface(IpMethod.Auto4),\n",
"], None, setup_params=setup_params)\n",
"\n",
"rp.set_local_portal(lp)\n",
"lp.set_remote_portal(rp)\n",
"\n",
"top_level_bridge = Bridge(*[\n",
" rp.get_interfaces()[0],\n",
" *lp.get_interfaces()[0:3],\n",
"])\n",
"\n",
"try:\n",
" runner.build(top_level_bridge)\n",
"\n",
" lp.get_interfaces()[0].set_rate(1)\n",
" lp.get_interfaces()[1].set_rate(1)\n",
" lp.get_interfaces()[2].set_rate(1)\n",
"\n",
" lp.speedtest_server()\n",
" directionInbound['Three1MBProxied'] = rp.speedtest_client('172.19.152.3')\n",
" rp.speedtest_server()\n",
" directionOutbound['Three1MBProxied'] = lp.speedtest_client('172.19.152.2')\n",
"finally:\n",
" runner.teardown()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"rp = RemotePortal([Interface(IpMethod.Auto4)], setup_params=setup_params)\n",
"lp = LocalPortal([\n",
" Interface(IpMethod.Auto4),\n",
" Interface(IpMethod.Auto4),\n",
" Interface(IpMethod.Auto4),\n",
" Interface(IpMethod.Auto4),\n",
"], None, setup_params=setup_params)\n",
"\n",
"rp.set_local_portal(lp)\n",
"lp.set_remote_portal(rp)\n",
"\n",
"top_level_bridge = Bridge(*[\n",
" rp.get_interfaces()[0],\n",
" *lp.get_interfaces()[0:4],\n",
"])\n",
"\n",
"try:\n",
" runner.build(top_level_bridge)\n",
"\n",
" lp.get_interfaces()[0].set_rate(1)\n",
" lp.get_interfaces()[1].set_rate(1)\n",
" lp.get_interfaces()[2].set_rate(1)\n",
" lp.get_interfaces()[3].set_rate(1)\n",
"\n",
" lp.speedtest_server()\n",
" directionInbound['Four1MBProxied'] = rp.speedtest_client('172.19.152.3')\n",
" rp.speedtest_server()\n",
" directionOutbound['Four1MBProxied'] = lp.speedtest_client('172.19.152.2')\n",
"finally:\n",
" runner.teardown()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Graphs\n",
"This section produces graphs from the collected data."
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
@ -127,34 +331,15 @@
] ]
}, },
{ {
"cell_type": "code", "cell_type": "markdown",
"execution_count": null,
"metadata": { "metadata": {
"pycharm": { "pycharm": {
"name": "#%%\n" "name": "#%% md\n"
} }
}, },
"outputs": [],
"source": [ "source": [
"# Manual results\n", "### Equal Connection Scaling\n",
"# sudo iperf3 -c X.X.X.X -t 30 -O 5 -J\n", "This section shows equal connections scaling at various speeds and number of connections."
"\n",
"import json\n",
"\n",
"def load_json_from_file(path):\n",
" with open(path, 'r') as f:\n",
" return json.loads(f.read())\n",
"\n",
"One1MBStraight = load_json_from_file('manual/One1MBStraight.json')\n",
"One2MBStraight = load_json_from_file('manual/One2MBStraight.json')\n",
"\n",
"Two1MBAggregate = load_json_from_file('manual/Two1MBAggregate.json')\n",
"Two2MBAggregate = load_json_from_file('manual/Two2MBAggregate.json')\n",
"\n",
"One1MBOne2MBAggregate = load_json_from_file('manual/One1MBOne2MBAggregate.json')\n",
"\n",
"# A 60 second long test\n",
"Two2MBAggregateKillOneRecoverOne = load_json_from_file('manual/Two2MBAggregateKillOneRecoverOne.json')\n"
] ]
}, },
{ {
@ -169,10 +354,10 @@
"source": [ "source": [
"plot_iperf_results(\n", "plot_iperf_results(\n",
" {\n", " {\n",
" '1x1MBps connection (not proxied)': One1MBStraight,\n", " '2x1MBps Connections (proxied)': directionInbound['Two1MBProxied'],\n",
" '2x1MBps connections (proxied)': Two1MBAggregate,\n", " '1x1MBps Connection (not proxied)': directionInbound['One1MBNotProxied'],\n",
" },\n", " },\n",
" 'Proxying adds additional bandwidth',\n", " 'Two Equal 1MB Connections',\n",
")" ")"
] ]
}, },
@ -188,12 +373,10 @@
"source": [ "source": [
"plot_iperf_results(\n", "plot_iperf_results(\n",
" {\n", " {\n",
" '1x1MBps connection (not proxied)': One1MBStraight,\n", " '2x2MBps Connections (proxied)': directionInbound['Two2MBProxied'],\n",
" '2x1MBps connections (proxied)': Two1MBAggregate,\n", " '1x2MBps Connection (not proxied)': directionInbound['One2MBNotProxied'],\n",
" '1x2MBps connection (not proxied)': One2MBStraight,\n",
" '2x2MBps connections (proxied)': Two2MBAggregate,\n",
" },\n", " },\n",
" 'Proxing bandwidth scaling/overhead',\n", " 'Two Equal 2MB Connections',\n",
")" ")"
] ]
}, },
@ -209,11 +392,43 @@
"source": [ "source": [
"plot_iperf_results(\n", "plot_iperf_results(\n",
" {\n", " {\n",
" '2x1MBps connections (proxied)': Two1MBAggregate,\n", " '4x1MBps Connections (proxied)': directionInbound['Four1MBProxied'],\n",
" '1x1MBps+1x2MBps connections (proxied)': One1MBOne2MBAggregate,\n", " '3x1MBps Connections (proxied)': directionInbound['Three1MBProxied'],\n",
" '2x2MBps connections (proxied)': Two2MBAggregate,\n", " '2x1MBps Connections (proxied)': directionInbound['Two1MBProxied'],\n",
" },\n", " },\n",
" 'Imbalanced connections add',\n", " 'More Equal Connections',\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"### Mixed Connections Scaling\n",
"This section shows mixed connections at various speeds with various events."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"plot_iperf_results(\n",
" {\n",
" '2x2MBps Connections (proxied)': directionInbound['Two2MBProxied'],\n",
" '1x1MBps + 1x2MBps Connections (proxied)': directionInbound['One1MBOne2MBProxied'],\n",
" '2x1MBps Connections (proxied)': directionInbound['Two1MBProxied'],\n",
" },\n",
" 'Mixed Speed Connections',\n",
")" ")"
] ]
}, },
@ -229,22 +444,29 @@
"source": [ "source": [
"plot_iperf_results(\n", "plot_iperf_results(\n",
" {\n", " {\n",
" '1x2MBps+1xYMBps connections (proxied)': Two2MBAggregateKillOneRecoverOne,\n", " '1x2MBps + 1xYMBps Connections (proxied)': directionInbound['One2MBOneYMBProxiedSlow15Return30'],\n",
" },\n", " },\n",
" 'Killed connection',\n", " 'Network Slow',\n",
" events={15: 'Y = 0', 40: 'Y = 2'},\n", " events={0: 'Y=2', 15: 'Y=1', 30: 'Y=2'}\n",
" filename='graph4.png',\n",
")" ")"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Criteria\n",
"This section automatically verifies some criteria with assertions."
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": { "metadata": {},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [], "outputs": [],
"source": [] "source": []
} }

View File

@ -1,8 +1,10 @@
import concurrent.futures
import ipaddress import ipaddress
import os import os
import re
import time import time
from datetime import datetime from datetime import datetime
from typing import Callable, List, Tuple from typing import Callable, List, Tuple, Optional, Union
from urllib.parse import quote from urllib.parse import quote
import proxmoxer import proxmoxer
@ -97,7 +99,7 @@ class ProxmoxRunner:
management_bridge: str, management_bridge: str,
management_initial_ip: ipaddress, management_initial_ip: ipaddress,
management_gateway: ipaddress, management_netmask: int = 24,
verify_ssl: bool = False, verify_ssl: bool = False,
): ):
@ -125,7 +127,7 @@ class ProxmoxRunner:
self._management_bridge = structure.Bridge() self._management_bridge = structure.Bridge()
self._management_bridge.set_name(management_bridge) self._management_bridge.set_name(management_bridge)
self._management_initial_ip = management_initial_ip self._management_initial_ip = management_initial_ip
self._management_gateway = management_gateway self._management_netmask = management_netmask
# generate a single use SSH key (we can use any with Proxmox) # generate a single use SSH key (we can use any with Proxmox)
self._private_key = paramiko.RSAKey.generate(3072) self._private_key = paramiko.RSAKey.generate(3072)
@ -136,13 +138,16 @@ class ProxmoxRunner:
self._build_bridges() self._build_bridges()
for node in nodes: with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
self._build_node(node) build_futures = [executor.submit(self._build_node, node) for node in nodes]
for future in build_futures:
future.result()
# guarantee that setup is not called until all of the nodes are built # guarantee that setup is not called until all of the nodes are built
# this means that all will have their final IPs by this point # this means that all will have their final IPs by this point
for node in nodes: setup_futures = [executor.submit(self._setup_node, node) for node in nodes]
self._setup_node(node) for future in setup_futures:
future.result()
def _await_task(self, upid, timeout=10): def _await_task(self, upid, timeout=10):
t1 = datetime.now() t1 = datetime.now()
@ -228,7 +233,14 @@ class ProxmoxRunner:
node.client.close() node.client.close()
del node.client del node.client
def ssh(self, node: structure.Node, command: str, error_stderr=False, error_stdout=False) -> int: def ssh(
self,
node: structure.Node,
command: str,
error_stderr=False,
error_stdout=False,
return_stdout=False,
) -> Union[int, str]:
chan = node.client.get_transport().open_session() chan = node.client.get_transport().open_session()
chan.exec_command(command) chan.exec_command(command)
@ -243,6 +255,12 @@ class ProxmoxRunner:
if error_stdout: if error_stdout:
raise Exception(chan.recv(2048).decode()) raise Exception(chan.recv(2048).decode())
if return_stdout is not False:
if return_stdout is True:
return chan.makefile().read()
else:
return chan.recv(return_stdout).decode()
return exit_status return exit_status
def _build_node(self, node: structure.Node): def _build_node(self, node: structure.Node):
@ -272,7 +290,7 @@ class ProxmoxRunner:
self._close_ssh(node) self._close_ssh(node)
stop_task = self._proxmox.nodes(self._proxmox_node).qemu(node.get_id()).status.shutdown.post() stop_task = self._proxmox.nodes(self._proxmox_node).qemu(node.get_id()).status.shutdown.post()
self._await_task(stop_task) self._await_task(stop_task, timeout=20)
# Step 3: connect to management bridge for final setup # Step 3: connect to management bridge for final setup
self._setup_node_interfaces(node) self._setup_node_interfaces(node)
@ -281,6 +299,7 @@ class ProxmoxRunner:
self._await_task(start_task) self._await_task(start_task)
self._open_ssh(node) self._open_ssh(node)
node.ssh = (lambda n: lambda *args, **kwargs: self.ssh(n, *args, **kwargs))(node)
def _setup_node_interfaces(self, node: structure.Node, interfaces: List[structure.Interface] = None): def _setup_node_interfaces(self, node: structure.Node, interfaces: List[structure.Interface] = None):
if interfaces is None: if interfaces is None:
@ -297,7 +316,7 @@ class ProxmoxRunner:
interface.set_bridge(self._management_bridge) interface.set_bridge(self._management_bridge)
addr = self._management_initial_ip + node.get_id() - self._initial_vm_id addr = self._management_initial_ip + node.get_id() - self._initial_vm_id
kwargs['ipconfig{}'.format(i)] = 'ip={}/24,gw={}'.format(addr, self._management_gateway) kwargs['ipconfig{}'.format(i)] = 'ip={}/{}'.format(addr, self._management_netmask)
interface.set_address(addr) interface.set_address(addr)
elif method == structure.IpMethod.Auto4: elif method == structure.IpMethod.Auto4:
bridge = interface.get_bridge() bridge = interface.get_bridge()
@ -311,9 +330,39 @@ class ProxmoxRunner:
raise RuntimeError('not implemented') raise RuntimeError('not implemented')
kwargs['net{}'.format(i)] = 'model=virtio,bridge={}'.format(interface.get_bridge().get_name()) kwargs['net{}'.format(i)] = 'model=virtio,bridge={}'.format(interface.get_bridge().get_name())
if interface.get_rate() is not None:
kwargs['net{}'.format(i)] += ',rate={}'.format(interface.get_rate())
def interface_set_rate(iface):
def new_set_rate(rate: Optional[int]):
structure.Interface.set_rate(iface, rate)
self._update_node_interfaces(node)
return new_set_rate
interface.set_rate = interface_set_rate(interface)
self._proxmox.nodes(self._proxmox_node).qemu(node.get_id()).config.put(**kwargs) self._proxmox.nodes(self._proxmox_node).qemu(node.get_id()).config.put(**kwargs)
def _update_node_interfaces(self, node: structure.Node):
interfaces = node.get_interfaces()
old_config = self._proxmox.nodes(self._proxmox_node).qemu(node.get_id()).config.get()
old_digest = old_config['digest']
old_config = {k: v for (k, v) in old_config.items() if k[:3] == 'net'}
rate_regex = re.compile(r',rate=(\d+(?:\.\d+)?)')
new_config = {'digest': old_digest}
for k, v in old_config.items():
index = int(k[3:])
iface = interfaces[index]
new_config[k] = rate_regex.sub('', v)
if iface.get_rate() is not None:
new_config[k] += ',rate={}'.format(iface.get_rate())
self._proxmox.nodes(self._proxmox_node).qemu(node.get_id()).config.put(**new_config)
def _setup_node(self, node: structure.Node): def _setup_node(self, node: structure.Node):
if node.get_setup() is not None: if node.get_setup() is not None:
self.ssh(node, node.get_setup(), error_stdout=True, error_stderr=True) self.ssh(node, node.get_setup(), error_stdout=True, error_stderr=True)

View File

@ -1,4 +1,5 @@
import ipaddress import ipaddress
import json
import textwrap import textwrap
from enum import Enum from enum import Enum
import random import random
@ -15,15 +16,15 @@ class IpMethod(Enum):
class Interface: class Interface:
def __init__(self, method: IpMethod, limit: Optional[int] = None): def __init__(self, method: IpMethod, rate: Optional[int] = None):
self._method: IpMethod self._method: IpMethod
self._node: Optional[Node] = None self._node: Optional[Node] = None
self._limit: Optional[int] = None self._rate: Optional[int] = None
self._bridge: Optional[Bridge] = None self._bridge: Optional[Bridge] = None
self._method = method self._method = method
self._limit = limit self._rate = rate
self._address: ipaddress.ip_address = None self._address: ipaddress.ip_address = None
def get_method(self): def get_method(self):
@ -47,6 +48,12 @@ class Interface:
def get_address(self) -> ipaddress.ip_address: def get_address(self) -> ipaddress.ip_address:
return self._address return self._address
def get_rate(self) -> Optional[int]:
return self._rate
def set_rate(self, rate: Optional[int]):
self._rate = rate
class Bridge: class Bridge:
def __init__(self, *interfaces: Interface): def __init__(self, *interfaces: Interface):
@ -78,6 +85,9 @@ class Bridge:
def get_ip_address(self) -> ipaddress.ip_address: def get_ip_address(self) -> ipaddress.ip_address:
return next(self._network_iterator) return next(self._network_iterator)
def get_network(self) -> str:
return str(ipaddress.ip_network('{}/{}'.format(self._addr, self.netmask), False))
class Node: class Node:
def __init__(self, interfaces: List[Interface], setup_params: Dict = None): def __init__(self, interfaces: List[Interface], setup_params: Dict = None):
@ -111,6 +121,9 @@ class Node:
def get_setup(self) -> Optional[str]: def get_setup(self) -> Optional[str]:
return None return None
def ssh(self, *args, **kwargs):
raise RuntimeError('ssh not implemented')
class SpeedTestServer(Node): class SpeedTestServer(Node):
def client(self, server: Interface): def client(self, server: Interface):
@ -144,17 +157,19 @@ class RemotePortal(Node):
./minio-client alias set s3 s3.us-west-001.backblazeb2.com {access_key} {secret_key} ./minio-client alias set s3 s3.us-west-001.backblazeb2.com {access_key} {secret_key}
./minio-client cp s3/dissertation/binaries/debian/{branch} mpbl3p ./minio-client cp s3/dissertation/binaries/debian/{branch} mpbl3p
cloud-init status --wait
sudo apt-get install -y iperf3
chmod +x mpbl3p chmod +x mpbl3p
cloud-init status --wait || cloud-init status --long
sudo apt-get install -y iperf3
''').format(**self.setup_params) ''').format(**self.setup_params)
def get_setup(self) -> Optional[str]: def get_setup(self) -> Optional[str]:
return textwrap.dedent(''' return textwrap.dedent('''
set -e set -e
sudo sysctl -w net.ipv4.conf.all.arp_announce=1
sudo sysctl -w net.ipv4.conf.all.arp_ignore=2
cat << EOF > config.ini cat << EOF > config.ini
[Host] [Host]
PrivateKey = INVALID PrivateKey = INVALID
@ -166,11 +181,27 @@ class RemotePortal(Node):
LocalHost = {local_host} LocalHost = {local_host}
LocalPort = 1234 LocalPort = 1234
EOF EOF
(nohup sudo ./mpbl3p > mpbl3p.log 2>&1 & echo $! > mpbl3p.pid)
sleep 1
sudo ip link set up nc0
sudo ip addr add 172.19.152.2/31 dev nc0
ps $(cat mpbl3p.pid)
''').format( ''').format(
local_host=self.get_interfaces()[0].get_address(), local_host=self.get_interfaces()[0].get_address(),
**self.setup_params, **self.setup_params,
) )
def speedtest_server(self):
self.ssh('iperf3 -s -1 -D', error_stdout=True, error_stderr=True)
def speedtest_client(self, target, time=30):
command = 'iperf3 -c {target} -t {time} -O 5 -J'.format(target=target, time=time)
out = self.ssh(command, error_stdout=True, error_stderr=True, return_stdout=True)
return json.loads(out)
class LocalPortal(Node): class LocalPortal(Node):
def __init__(self, wan_interfaces: List[Interface], child: Optional[Node], **kwargs): def __init__(self, wan_interfaces: List[Interface], child: Optional[Node], **kwargs):
@ -197,11 +228,10 @@ class LocalPortal(Node):
./minio-client alias set s3 s3.us-west-001.backblazeb2.com {access_key} {secret_key} ./minio-client alias set s3 s3.us-west-001.backblazeb2.com {access_key} {secret_key}
./minio-client cp s3/dissertation/binaries/debian/{branch} mpbl3p ./minio-client cp s3/dissertation/binaries/debian/{branch} mpbl3p
cloud-init status --wait
sudo apt-get install -y iperf3
chmod +x mpbl3p chmod +x mpbl3p
cloud-init status --wait || cloud-init status --long
sudo apt-get install -y iperf3
''').format(**self.setup_params) ''').format(**self.setup_params)
def get_setup(self) -> str: def get_setup(self) -> str:
@ -221,13 +251,47 @@ class LocalPortal(Node):
remote_host=self.remote_portal.get_interfaces()[0].get_address(), remote_host=self.remote_portal.get_interfaces()[0].get_address(),
) for x in self.get_interfaces()[:-1]]) ) for x in self.get_interfaces()[:-1]])
policy_routing_string = textwrap.dedent('''
sudo ip route flush {table_number}
sudo ip route add table {table_number} to {network} dev {device}
sudo ip rule add from {local_address} table {table_number} priority {table_number}
''')
policy_routing = '\n\n'.join([policy_routing_string.format(
table_number=i+10,
device='eth{}'.format(i),
network=iface.get_bridge().get_network(),
local_address=iface.get_address(),
) for i, iface in enumerate(self.get_interfaces()[:-1])])
return textwrap.dedent(''' return textwrap.dedent('''
set -e set -e
sudo sysctl -w net.ipv4.conf.all.arp_announce=1
sudo sysctl -w net.ipv4.conf.all.arp_ignore=2
{policy_routing}
cat << EOF > config.ini cat << EOF > config.ini
[Host] [Host]
PrivateKey = INVALID PrivateKey = INVALID
{peers} {peers}
EOF EOF
''').format(**self.setup_params, peers=peers)
(nohup sudo ./mpbl3p > mpbl3p.log 2>&1 & echo $! > mpbl3p.pid)
sleep 1
sudo ip link set up nc0
sudo ip addr add 172.19.152.3/31 dev nc0
ps $(cat mpbl3p.pid)
''').format(**self.setup_params, peers=peers, policy_routing=policy_routing)
def speedtest_server(self):
self.ssh('iperf3 -s -1 -D', error_stdout=True, error_stderr=True)
def speedtest_client(self, target, time=30):
command = 'iperf3 -c {target} -t {time} -O 5 -J'.format(target=target, time=time)
out = self.ssh(command, error_stdout=True, error_stderr=True, return_stdout=True)
return json.loads(out)