Commit 3efd1b15 authored by Marvin Meiers's avatar Marvin Meiers Committed by Antoine Kaufmann
Browse files

experiments: split up topologies in ns3 end-to-end framework

Use the new basic building blocks SwitchNode and SimpleChannel in the
ns3 end-to-end framework to build topologies.
parent e28dfebd
...@@ -25,6 +25,7 @@ import simbricks.orchestration.nodeconfig as node ...@@ -25,6 +25,7 @@ import simbricks.orchestration.nodeconfig as node
import simbricks.orchestration.simulators as sim import simbricks.orchestration.simulators as sim
import simbricks.orchestration.e2e_components as e2e import simbricks.orchestration.e2e_components as e2e
from simbricks.orchestration.simulator_utils import create_tcp_cong_hosts from simbricks.orchestration.simulator_utils import create_tcp_cong_hosts
from simbricks.orchestration.e2e_topologies import E2EDumbbellTopology
# iperf TCP_multi_client test # iperf TCP_multi_client test
# naming convention following host-nic-net-app # naming convention following host-nic-net-app
...@@ -76,17 +77,18 @@ for congestion_control in types_of_congestion_control: ...@@ -76,17 +77,18 @@ for congestion_control in types_of_congestion_control:
net = NetClass() net = NetClass()
net.opt = ' '.join([f'--{o[0]}={o[1]}' for o in options.items()]) net.opt = ' '.join([f'--{o[0]}={o[1]}' for o in options.items()])
topology = e2e.E2EDumbbellTopology('topo')
topology = E2EDumbbellTopology()
topology.data_rate = f'{link_rate}Mbps' topology.data_rate = f'{link_rate}Mbps'
topology.delay = f'{link_latency}ms' topology.delay = f'{link_latency}ms'
topology.queue_size = f'{queue_size}B' topology.queue_size = f'{queue_size}B'
net.e2e_components.append(topology) topology.mtu = f'{mtu-52}'
net.add_component(topology)
for i in range(1, num_ns3_hosts + 1): for i in range(1, num_ns3_hosts + 1):
host = e2e.E2ESimpleNs3Host(f'ns3server-{i}') host = e2e.E2ESimpleNs3Host(f'ns3server-{i}')
host.delay = '1us' host.delay = '1us'
host.data_rate = f'{link_rate}Mbps' host.data_rate = f'{link_rate}Mbps'
host.node_position = 'left'
host.ip = f'192.168.64.{i}/24' host.ip = f'192.168.64.{i}/24'
host.queue_size = f'{queue_size}B' host.queue_size = f'{queue_size}B'
app = e2e.E2EPacketSinkApplication('sink') app = e2e.E2EPacketSinkApplication('sink')
...@@ -97,20 +99,19 @@ for congestion_control in types_of_congestion_control: ...@@ -97,20 +99,19 @@ for congestion_control in types_of_congestion_control:
probe.interval = '100ms' probe.interval = '100ms'
probe.file = f'sink-rx-{i}' probe.file = f'sink-rx-{i}'
app.add_component(probe) app.add_component(probe)
topology.add_component(host) topology.add_left_component(host)
for i in range(1, num_ns3_hosts + 1): for i in range(1, num_ns3_hosts + 1):
host = e2e.E2ESimpleNs3Host(f'ns3client-{i}') host = e2e.E2ESimpleNs3Host(f'ns3client-{i}')
host.delay = '1us' host.delay = '1us'
host.data_rate = f'{link_rate}Mbps' host.data_rate = f'{link_rate}Mbps'
host.node_position = 'right'
host.ip = f'192.168.64.{i+num_ns3_hosts+num_simbricks_hosts}/24' host.ip = f'192.168.64.{i+num_ns3_hosts+num_simbricks_hosts}/24'
host.queue_size = f'{queue_size}B' host.queue_size = f'{queue_size}B'
app = e2e.E2EBulkSendApplication('sender') app = e2e.E2EBulkSendApplication('sender')
app.remote_ip = f'192.168.64.{i}:5000' app.remote_ip = f'192.168.64.{i}:5000'
app.stop_time = '20s' app.stop_time = '20s'
host.add_component(app) host.add_component(app)
topology.add_component(host) topology.add_right_component(host)
e = exp.Experiment( e = exp.Experiment(
'gt-ib-dumbbell-' + str(congestion_control) + 'TCPm' + 'gt-ib-dumbbell-' + str(congestion_control) + 'TCPm' +
...@@ -164,16 +165,14 @@ for congestion_control in types_of_congestion_control: ...@@ -164,16 +165,14 @@ for congestion_control in types_of_congestion_control:
for i, server in enumerate(servers, 1): for i, server in enumerate(servers, 1):
host = e2e.E2ESimbricksHost(f'simbricksserver-{i}') host = e2e.E2ESimbricksHost(f'simbricksserver-{i}')
host.eth_latency = '1us' host.eth_latency = '1us'
host.node_position = 'left'
host.simbricks_host = server.nics[0] host.simbricks_host = server.nics[0]
topology.add_component(host) topology.add_left_component(host)
for i, client in enumerate(clients, 1): for i, client in enumerate(clients, 1):
host = e2e.E2ESimbricksHost(f'simbricksclient-{i}') host = e2e.E2ESimbricksHost(f'simbricksclient-{i}')
host.eth_latency = '1us' host.eth_latency = '1us'
host.node_position = 'right'
host.simbricks_host = client.nics[0] host.simbricks_host = client.nics[0]
topology.add_component(host) topology.add_right_component(host)
i = 0 i = 0
for cl in clients: for cl in clients:
......
...@@ -142,27 +142,55 @@ class E2EComponent(E2EBase): ...@@ -142,27 +142,55 @@ class E2EComponent(E2EBase):
component.resolve_paths() component.resolve_paths()
class E2ETopology(E2EComponent): class E2ETopologyNode(E2EComponent):
def __init__(self, idd: str) -> None: def __init__(self, idd: str) -> None:
super().__init__(idd) super().__init__(idd)
self.category = "Topology" self.category = "TopologyNode"
class E2EDumbbellTopology(E2ETopology): class E2ESwitchNode(E2ETopologyNode):
def __init__(self, idd: str) -> None: def __init__(self, idd: str) -> None:
super().__init__(idd) super().__init__(idd)
self.type = "Dumbbell" self.type = "Switch"
self.mtu = ""
def ns3_config(self) -> str:
self.mapping.update({
"Mtu": self.mtu,
})
return super().ns3_config()
class E2ETopologyChannel(E2EComponent):
def __init__(self, idd: str) -> None:
super().__init__(idd)
self.category = "TopologyChannel"
class E2ESimpleChannel(E2ETopologyChannel):
def __init__(self, idd: str) -> None:
super().__init__(idd)
self.type = "Simple"
self.data_rate = "" self.data_rate = ""
self.queue_size = "" self.queue_size = ""
self.delay = "" self.delay = ""
self.left_node: E2ETopologyNode
self.right_node: E2ETopologyNode
def ns3_config(self) -> str: def ns3_config(self) -> str:
if self.left_node is None or self.right_node is None:
print(f"Not all nodes for channel {self.id} given")
sys.exit(1)
self.mapping.update({ self.mapping.update({
"DataRate": self.data_rate, "DataRate": self.data_rate,
"QueueSize": self.queue_size, "QueueSize": self.queue_size,
"Delay": self.delay, "Delay": self.delay,
"LeftNode": self.left_node.id,
"RightNode": self.right_node.id,
}) })
return super().ns3_config() return super().ns3_config()
...@@ -172,11 +200,6 @@ class E2EHost(E2EComponent): ...@@ -172,11 +200,6 @@ class E2EHost(E2EComponent):
def __init__(self, idd: str) -> None: def __init__(self, idd: str) -> None:
super().__init__(idd) super().__init__(idd)
self.category = "Host" self.category = "Host"
self.node_position = ""
def ns3_config(self) -> str:
self.mapping.update({"NodePosition": self.node_position})
return super().ns3_config()
class E2ESimbricksHost(E2EHost): class E2ESimbricksHost(E2EHost):
......
# Copyright 2023 Max Planck Institute for Software Systems, and
# National University of Singapore
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Allow own class to be used as type for a method's argument
from __future__ import annotations
from abc import ABC, abstractmethod
import simbricks.orchestration.e2e_components as e2e
class E2ETopology(ABC):
@abstractmethod
def add_to_network(self, net):
pass
class E2EDumbbellTopology(E2ETopology):
def __init__(self):
self.left_switch = e2e.E2ESwitchNode("_leftSwitch")
self.right_switch = e2e.E2ESwitchNode("_rightSwitch")
self.link = e2e.E2ESimpleChannel("_link")
self.link.left_node = self.left_switch
self.link.right_node = self.right_switch
def add_to_network(self, net):
net.add_component(self.left_switch)
net.add_component(self.right_switch)
net.add_component(self.link)
def add_left_component(self, component: e2e.E2EComponent):
self.left_switch.add_component(component)
def add_right_component(self, component: e2e.E2EComponent):
self.right_switch.add_component(component)
@property
def mtu(self):
return self.left_switch.mtu
@mtu.setter
def mtu(self, mtu: str):
self.left_switch.mtu = mtu
self.right_switch.mtu = mtu
@property
def data_rate(self):
return self.link.data_rate
@data_rate.setter
def data_rate(self, data_rate: str):
self.link.data_rate = data_rate
@property
def queue_size(self):
return self.link.queue_size
@queue_size.setter
def queue_size(self, queue_size: str):
self.link.queue_size = queue_size
@property
def delay(self):
return self.link.delay
@delay.setter
def delay(self, delay: str):
self.link.delay = delay
...@@ -29,7 +29,8 @@ import typing as tp ...@@ -29,7 +29,8 @@ import typing as tp
from simbricks.orchestration.experiment.experiment_environment import ExpEnv from simbricks.orchestration.experiment.experiment_environment import ExpEnv
from simbricks.orchestration.nodeconfig import NodeConfig from simbricks.orchestration.nodeconfig import NodeConfig
from simbricks.orchestration.e2e_components import E2ETopology, E2ESimbricksHost from simbricks.orchestration.e2e_topologies import E2ETopology
from simbricks.orchestration import e2e_components as e2e
class Simulator(object): class Simulator(object):
...@@ -892,10 +893,24 @@ class NS3E2ENet(NetSim): ...@@ -892,10 +893,24 @@ class NS3E2ENet(NetSim):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self.e2e_components: tp.List[E2ETopology] = [] self.first_run = True
self.e2e_components: tp.List[tp.Union[e2e.E2ETopologyNode,
e2e.E2ETopologyChannel]] = []
self.e2e_topologies: tp.List[E2ETopology] = []
def add_component(
self,
component: tp.Union[e2e.E2ETopologyNode,
e2e.E2ETopologyChannel,
E2ETopology]
):
if isinstance(component, E2ETopology):
self.e2e_topologies.append(component)
else:
self.e2e_components.append(component)
def resolve_socket_paths( def resolve_socket_paths(
self, env: ExpEnv, e2e_sim: E2ESimbricksHost self, env: ExpEnv, e2e_sim: e2e.E2ESimbricksHost
) -> None: ) -> None:
if e2e_sim.simbricks_host is None: if e2e_sim.simbricks_host is None:
print('E2E Simbricks host does not contain a simulator') print('E2E Simbricks host does not contain a simulator')
...@@ -903,13 +918,19 @@ class NS3E2ENet(NetSim): ...@@ -903,13 +918,19 @@ class NS3E2ENet(NetSim):
e2e_sim.unix_socket = env.nic_eth_path(e2e_sim.simbricks_host) e2e_sim.unix_socket = env.nic_eth_path(e2e_sim.simbricks_host)
def run_cmd(self, env): def run_cmd(self, env):
for topo in self.e2e_components: if self.first_run:
if not topo.has_path: for topo in self.e2e_topologies:
topo.resolve_paths() topo.add_to_network(self)
for c in topo.components:
if isinstance(c, E2ESimbricksHost): for component in self.e2e_components:
if self.first_run:
component.resolve_paths()
for c in component.components:
if isinstance(c, e2e.E2ESimbricksHost):
self.resolve_socket_paths(env, c) self.resolve_socket_paths(env, c)
self.first_run = False
params: tp.List[str] = [] params: tp.List[str] = []
for component in self.e2e_components: for component in self.e2e_components:
params.append(component.ns3_config()) params.append(component.ns3_config())
......
Subproject commit ad29fd3e44396b197865cc04ba748813b21aa35d Subproject commit 728d52c54bdfa8d8c1a3fa2300ea932ea9932640
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment