Commit 1dd38d6c authored by Marvin Meiers's avatar Marvin Meiers Committed by Antoine Kaufmann
Browse files

experiments: add support for connecting networks to NS3E2ENet

This adds a new component E2ESimbricksNetwork to the end-to-end
framework, which allows to connect to other simulated networks through
Simbricks.
parent 32133d9f
# 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.
import simbricks.orchestration.experiments as exp
import simbricks.orchestration.nodeconfig as node
import simbricks.orchestration.simulators as sim
import simbricks.orchestration.e2e_components as e2e
from simbricks.orchestration.simulator_utils import create_tcp_cong_hosts
# iperf TCP_multi_client test
# naming convention following host-nic-net-app
# host: qemu/gem5-timing
# nic: cv/cb/ib
# net: switch/dumbbell/bridge
# app: DCTCPm
#types_of_host = ['qemu', 'qt', 'gt', 'gO3']
types_of_host = ['gt']
#types_of_nic = ['cv', 'cb', 'ib']
types_of_nic = ['ib']
#types_of_net = ['dumbbell']
#types_of_app = ['DCTCPm']
types_of_mtu = [1500]
types_of_congestion_control = [e2e.CongestionControl.CUBIC]
num_ns3_hosts = 1
num_simbricks_hosts = 1
#max_k = 199680
#k_step = 16640
#k_step = 33280
link_rate = 1000 # in Mbps
link_latency = 500 # in ns
bdp = int(link_rate * link_latency / 10**9 * 10**6) # Bandwidth-delay product
cpu_freq = '5GHz'
cpu_freq_qemu = '2GHz'
sys_clock = '1GHz' # if not set, default 1GHz
ip_start = '192.168.64.1'
experiments = []
# set network sim
NetClass = sim.NS3E2ENet
for congestion_control in types_of_congestion_control:
for mtu in types_of_mtu:
for k_val in range(0, 1):
queue_size = int(bdp * 2**k_val)
options = {
'ns3::TcpL4Protocol::SocketType': congestion_control.ns3,
'ns3::TcpSocket::SegmentSize': f'{mtu-52}',
'ns3::TcpSocket::SndBufSize': '524288',
'ns3::TcpSocket::RcvBufSize': '524288',
}
left_net = NetClass()
left_net.name = 'left_net'
left_net.opt = ' '.join([
f'--{o[0]}={o[1]}' for o in options.items()
])
right_net = NetClass()
right_net.name = 'right_net'
right_net.opt = ' '.join([
f'--{o[0]}={o[1]}' for o in options.items()
])
# left connects -> NetIf
# right created socket -> NicIf
left_switch = e2e.E2ESwitchNode('left_switch')
left_switch.mtu = f'{mtu-52}'
left_net.add_component(left_switch)
right_switch = e2e.E2ESwitchNode('right_switch')
right_switch.mtu = f'{mtu-52}'
right_net.add_component(right_switch)
left_adapter = e2e.E2ESimbricksNetworkNetIf('left_adapter')
left_adapter.eth_latency = f'{link_latency}ns'
left_adapter.simbricks_component = right_net
left_switch.add_component(left_adapter)
right_adapter = e2e.E2ESimbricksNetworkNicIf('right_adapter')
right_adapter.eth_latency = f'{link_latency}ns'
right_adapter.simbricks_component = left_net
right_switch.add_component(right_adapter)
for i in range(1, num_ns3_hosts + 1):
host = e2e.E2ESimpleNs3Host(f'ns3server-{i}')
host.delay = '1us'
host.data_rate = f'{link_rate}Mbps'
host.ip = f'192.168.64.{i}/24'
host.queue_size = f'{queue_size}B'
app = e2e.E2EPacketSinkApplication('sink')
app.local_ip = '0.0.0.0:5000'
app.stop_time = '20s'
host.add_component(app)
probe = e2e.E2EPeriodicSampleProbe('probe', 'Rx')
probe.interval = '100ms'
probe.file = f'sink-rx-{i}'
app.add_component(probe)
left_switch.add_component(host)
for i in range(1, num_ns3_hosts + 1):
host = e2e.E2ESimpleNs3Host(f'ns3client-{i}')
host.delay = '1us'
host.data_rate = f'{link_rate}Mbps'
host.ip = f'192.168.64.{i+num_ns3_hosts+num_simbricks_hosts}/24'
host.queue_size = f'{queue_size}B'
app = e2e.E2EBulkSendApplication('sender')
app.remote_ip = f'192.168.64.{i}:5000'
app.stop_time = '20s'
host.add_component(app)
right_switch.add_component(host)
e = exp.Experiment(
'gt-ib-dumbbell-' + str(congestion_control) + 'TCPm' +
f'{k_val}' + f'-{mtu}'
)
e.add_network(left_net)
e.add_network(right_net)
freq = cpu_freq
# simbricks host
def gem5_timing(node_config: node.NodeConfig):
h = sim.Gem5Host(node_config)
#h.sys_clock = sys_clock
return h
HostClass = gem5_timing
e.checkpoint = True
NicClass = sim.I40eNIC
NcClass = node.I40eTCPCongNode
servers = create_tcp_cong_hosts(
e,
num_simbricks_hosts,
'server',
left_net,
NicClass,
HostClass,
NcClass,
node.TcpCongServer,
freq,
mtu,
congestion_control.gem5,
ip_start=num_ns3_hosts + 1
)
clients = create_tcp_cong_hosts(
e,
num_simbricks_hosts,
'client',
right_net,
NicClass,
HostClass,
NcClass,
node.TcpCongClient,
freq,
mtu,
congestion_control.gem5,
ip_start=2 * num_ns3_hosts + num_simbricks_hosts + 1
)
for i, server in enumerate(servers, 1):
host = e2e.E2ESimbricksHost(f'simbricksserver-{i}')
host.eth_latency = '1us'
host.simbricks_component = server.nics[0]
left_switch.add_component(host)
for i, client in enumerate(clients, 1):
host = e2e.E2ESimbricksHost(f'simbricksclient-{i}')
host.eth_latency = '1us'
host.simbricks_component = client.nics[0]
right_switch.add_component(host)
i = 0
for cl in clients:
cl.node_config.app.server_ip = servers[i].node_config.ip
i += 1
# All the clients will not poweroff after finishing iperf test
# except the last one This is to prevent the simulation gets
# stuck when one of host exits.
# The last client waits for the output printed in other hosts,
# then cleanup
clients[num_simbricks_hosts - 1].node_config.app.is_last = True
clients[num_simbricks_hosts - 1].wait = True
print(e.name)
experiments.append(e)
...@@ -68,6 +68,18 @@ class CongestionControl(Enum): ...@@ -68,6 +68,18 @@ class CongestionControl(Enum):
return self.gem5_str return self.gem5_str
class SimbricksAdapterType(Enum):
NIC = 0
NETWORK = 1
HOST = 2
class SimbricksSyncMode(Enum):
SYNC_DISABLED = 0
SYNC_OPTIONAL = 1
SYNC_REQUIRED = 2
class E2EBase(ABC): class E2EBase(ABC):
def __init__(self) -> None: def __init__(self) -> None:
...@@ -195,6 +207,45 @@ class E2ESimpleChannel(E2ETopologyChannel): ...@@ -195,6 +207,45 @@ class E2ESimpleChannel(E2ETopologyChannel):
return super().ns3_config() return super().ns3_config()
class E2ESimbricksNetwork(E2EComponent):
def __init__(self, idd: str) -> None:
super().__init__(idd)
self.category = "Network"
self.adapter_type = SimbricksAdapterType.NETWORK
self.unix_socket = ""
self.sync_delay = ""
self.poll_delay = ""
self.eth_latency = ""
self.sync: SimbricksSyncMode = SimbricksSyncMode.SYNC_OPTIONAL
self.simbricks_component = None
def ns3_config(self) -> str:
self.mapping.update({
"UnixSocket": self.unix_socket,
"SyncDelay": self.sync_delay,
"PollDelay": self.poll_delay,
"EthLatency": self.eth_latency,
"Sync": "" if self.sync is None else f"{self.sync.value}",
})
return super().ns3_config()
class E2ESimbricksNetworkNetIf(E2ESimbricksNetwork):
def __init__(self, idd: str) -> None:
super().__init__(idd)
self.type = "NetIf"
class E2ESimbricksNetworkNicIf(E2ESimbricksNetwork):
def __init__(self, idd: str) -> None:
super().__init__(idd)
self.type = "NicIf"
class E2EHost(E2EComponent): class E2EHost(E2EComponent):
def __init__(self, idd: str) -> None: def __init__(self, idd: str) -> None:
...@@ -207,13 +258,14 @@ class E2ESimbricksHost(E2EHost): ...@@ -207,13 +258,14 @@ class E2ESimbricksHost(E2EHost):
def __init__(self, idd: str) -> None: def __init__(self, idd: str) -> None:
super().__init__(idd) super().__init__(idd)
self.type = "Simbricks" self.type = "Simbricks"
self.adapter_type = SimbricksAdapterType.NIC
self.unix_socket = "" self.unix_socket = ""
self.sync_delay = "" self.sync_delay = ""
self.poll_delay = "" self.poll_delay = ""
self.eth_latency = "" self.eth_latency = ""
self.sync = True self.sync: SimbricksSyncMode = SimbricksSyncMode.SYNC_OPTIONAL
self.simbricks_host = None self.simbricks_component = None
def ns3_config(self) -> str: def ns3_config(self) -> str:
self.mapping.update({ self.mapping.update({
...@@ -221,7 +273,7 @@ class E2ESimbricksHost(E2EHost): ...@@ -221,7 +273,7 @@ class E2ESimbricksHost(E2EHost):
"SyncDelay": self.sync_delay, "SyncDelay": self.sync_delay,
"PollDelay": self.poll_delay, "PollDelay": self.poll_delay,
"EthLatency": self.eth_latency, "EthLatency": self.eth_latency,
"Sync": "1" if self.sync else "0", "Sync": "" if self.sync is None else f"{self.sync.value}",
}) })
return super().ns3_config() return super().ns3_config()
......
...@@ -911,12 +911,29 @@ class NS3E2ENet(NetSim): ...@@ -911,12 +911,29 @@ class NS3E2ENet(NetSim):
self.e2e_components.append(component) self.e2e_components.append(component)
def resolve_socket_paths( def resolve_socket_paths(
self, env: ExpEnv, e2e_sim: e2e.E2ESimbricksHost self,
env: ExpEnv,
e2e_sim: tp.Union[e2e.E2ESimbricksNetwork, e2e.E2ESimbricksHost],
listen: bool = False
) -> None: ) -> None:
if e2e_sim.simbricks_host is None: if e2e_sim.simbricks_component is None:
print('E2E Simbricks host does not contain a simulator') print('E2E Simbricks adapter does not contain a simulator')
sys.exit(1) sys.exit(1)
e2e_sim.unix_socket = env.nic_eth_path(e2e_sim.simbricks_host) if e2e_sim.adapter_type == e2e.SimbricksAdapterType.NIC:
e2e_sim.unix_socket = env.nic_eth_path(e2e_sim.simbricks_component)
elif e2e_sim.adapter_type == e2e.SimbricksAdapterType.NETWORK:
if listen:
e2e_sim.unix_socket = env.n2n_eth_path(
self, e2e_sim.simbricks_component
)
else:
e2e_sim.unix_socket = env.n2n_eth_path(
e2e_sim.simbricks_component, self
)
elif e2e_sim.adapter_type == e2e.SimbricksAdapterType.HOST:
e2e_sim.unix_socket = env.net2host_eth_path(
self, e2e_sim.simbricks_component
)
def run_cmd(self, env): def run_cmd(self, env):
if self.first_run: if self.first_run:
...@@ -926,9 +943,16 @@ class NS3E2ENet(NetSim): ...@@ -926,9 +943,16 @@ class NS3E2ENet(NetSim):
for component in self.e2e_components: for component in self.e2e_components:
if self.first_run: if self.first_run:
component.resolve_paths() component.resolve_paths()
for c in component.components: for c in component.components:
if isinstance(c, e2e.E2ESimbricksHost): if isinstance(c, e2e.E2ESimbricksHost):
self.resolve_socket_paths(env, c) self.resolve_socket_paths(env, c)
elif isinstance(c, e2e.E2ESimbricksNetworkNetIf):
self.resolve_socket_paths(env, c)
if self.first_run:
self.connect_network(c.simbricks_component)
elif isinstance(c, e2e.E2ESimbricksNetworkNicIf):
self.resolve_socket_paths(env, c, True)
self.first_run = False self.first_run = False
......
Subproject commit 10bfccbfc097a827b52d2b6a9a73f51bf81101b6 Subproject commit 4d3b44a2bb0898a5fe54a1ebac8eab4a93e50259
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