115 lines
4.0 KiB
Python
115 lines
4.0 KiB
Python
import threading
|
|
from typing import Tuple, Optional
|
|
|
|
from structure import Bridge, StandardTest, SpeedTestServer, Node, IperfResult, RemotePortal, Interface, \
|
|
IpMethod, LocalPortal
|
|
from structure.tests import repeat_until_satisfied
|
|
|
|
|
|
class BaseEnvironment:
|
|
def __init__(self, runner, top_level_bridge: Bridge):
|
|
self.top_level_bridge = top_level_bridge
|
|
self._runner = runner
|
|
|
|
def __enter__(self):
|
|
try:
|
|
self._runner.build(self.top_level_bridge)
|
|
except Exception as e:
|
|
self._runner.teardown()
|
|
raise e
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
self._runner.teardown()
|
|
|
|
def _test(
|
|
self,
|
|
test: StandardTest,
|
|
inbound_server: SpeedTestServer,
|
|
inbound_client: SpeedTestServer,
|
|
rated_node: Node,
|
|
expected_interfaces: int,
|
|
) -> Tuple[IperfResult, IperfResult]:
|
|
if len(test.rates) != expected_interfaces:
|
|
raise RuntimeError('mismatched number of interfaces')
|
|
|
|
results = []
|
|
for server, client in [(inbound_server, inbound_client), (inbound_client, inbound_server)]:
|
|
def test_reducer(old: Optional[IperfResult]) -> IperfResult:
|
|
for i, r in enumerate(test.rates):
|
|
rated_node.get_interfaces()[i].set_rate(r)
|
|
server.server()
|
|
|
|
for delay, (index, rate) in test.events.items():
|
|
iface = rated_node.get_interfaces()[index]
|
|
threading.Timer(6 + delay, iface.set_rate, args=[rate]).start()
|
|
|
|
iperf = client.client(server, time=test.duration)
|
|
if old is None:
|
|
return IperfResult(test, iperf)
|
|
else:
|
|
old.add_results(iperf)
|
|
return old
|
|
|
|
def test_satisfier(val: IperfResult) -> bool:
|
|
if val.num_tests < 3:
|
|
return False
|
|
return val.bandwidth_coefficient_variance() < test.bandwidth_variation_target and False not in \
|
|
[x < test.interval_variation_target for x in val.interval_coefficient_variances().values()]
|
|
|
|
result = repeat_until_satisfied(
|
|
test_reducer,
|
|
test_satisfier,
|
|
max_failures=test.max_failures,
|
|
max_attempts=test.max_attempts,
|
|
)
|
|
results.append(result)
|
|
|
|
# Return a tuple of (inbound, outbound)
|
|
return results[0], results[1]
|
|
|
|
def test(self, test: StandardTest) -> Tuple[IperfResult, IperfResult]:
|
|
raise RuntimeError('not implemented')
|
|
|
|
|
|
class StandardEnvironment(BaseEnvironment):
|
|
def __init__(self, interfaces: int, runner, setup_params: dict):
|
|
self._interfaces = interfaces
|
|
|
|
self.rp = RemotePortal([Interface(IpMethod.Auto4)], setup_params=setup_params)
|
|
|
|
self.st = SpeedTestServer()
|
|
self.cl = SpeedTestServer(clone_interface=self.rp.get_interfaces()[0])
|
|
|
|
self.lp = LocalPortal(
|
|
[Interface(IpMethod.Auto4) for _ in range(interfaces)],
|
|
self.cl,
|
|
setup_params=setup_params,
|
|
)
|
|
|
|
self.rp.set_local_portal(self.lp)
|
|
self.lp.set_remote_portal(self.rp)
|
|
|
|
super().__init__(runner, Bridge(
|
|
self.st.get_interfaces()[0],
|
|
self.rp.get_interfaces()[0],
|
|
*self.lp.get_interfaces()[0:interfaces],
|
|
))
|
|
|
|
def test(self, test: StandardTest) -> Tuple[IperfResult, IperfResult]:
|
|
return self._test(test, self.st, self.cl, self.lp, self._interfaces)
|
|
|
|
|
|
class DirectEnvironment(BaseEnvironment):
|
|
def __init__(self, runner):
|
|
self.st1 = SpeedTestServer()
|
|
self.st2 = SpeedTestServer()
|
|
|
|
super().__init__(runner, Bridge(
|
|
self.st1.get_interfaces()[0],
|
|
self.st2.get_interfaces()[0],
|
|
))
|
|
|
|
def test(self, test: StandardTest) -> Tuple[IperfResult, IperfResult]:
|
|
return self._test(test, self.st2, self.st1, self.st2, 1)
|