Unverified Commit d910c30f authored by Jakob Görgen's avatar Jakob Görgen
Browse files

experiments/simbricks/orchestration/instantiation/base : updated socket path...

experiments/simbricks/orchestration/instantiation/base : updated socket path creation + experiments/simbricks/orchestration/simulation/net/base: added new simulator
parent 68e9384f
...@@ -61,7 +61,7 @@ class Instantiation: ...@@ -61,7 +61,7 @@ class Instantiation:
): ):
self._simulation = simulation self._simulation = simulation
self._env: InstantiationEnvironment = env self._env: InstantiationEnvironment = env
self._socket_tracker: dict[simulation.channel.Channel, SockType] = {} self._socket_tracker: dict[simulation.channel.Channel, Socket] = {}
@staticmethod @staticmethod
def is_absolute_exists(path: str) -> bool: def is_absolute_exists(path: str) -> bool:
...@@ -96,47 +96,85 @@ class Instantiation: ...@@ -96,47 +96,85 @@ class Instantiation:
# def proxy_shm_path(self, sim: "simulators.Simulator") -> str: # def proxy_shm_path(self, sim: "simulators.Simulator") -> str:
# return f"{self.shm_base}/proxy.shm.{sim.name}" # return f"{self.shm_base}/proxy.shm.{sim.name}"
def _get_chan_by_interface(
self, interface: system.base.Interface
) -> simulation.channel.Channel:
if not interface.is_connected():
raise Exception(
"cannot determine channel by interface, interface isn't connecteds"
)
channel = interface.channel
return channel
def _get_socket_by_channel(
self, channel: simualtion.channel.Channel
) -> Socket | None:
if not channel in self._socket_tracker:
return None
return self._socket_tracker[channel]
def _updated_tracker_mapping(
self, interface: system.base.Interface, socket: Socket
) -> None:
channel = self._get_chan_by_interface(interface=interface)
if channel in self._socket_tracker:
raise Exception(
"cannot update socket tracker mapping, channel is already mapped"
)
self._socket_tracker[channel] = socket
def _get_socket_by_interface(self, interface: system.base.Interface) -> str | None:
# TODO
pass
def _interface_to_sock_path(self, interface: system.base.Interface) -> str: def _interface_to_sock_path(self, interface: system.base.Interface) -> str:
basepath = pathlib.Path(self._env._workdir) basepath = pathlib.Path(self._env._workdir)
channel = self._get_chan_by_interface(interface=interface)
sys_channel = channel.sys_channel
queue_ident = f"{sys_channel.a._id}.{sys_channel._id}.{sys_channel.b._id}"
queue_type = None
match interface: match interface:
case PCIeHostInterface() | PCIeDeviceInterface(): case PCIeHostInterface() | PCIeDeviceInterface():
return f"{self._env._shm_base}/shm.pci/{interface.component.name}.{interface._id}" queue_type = "shm.pci"
case MemDeviceInterface() | MemHostInterface(): case MemDeviceInterface() | MemHostInterface():
return f"{self._env._shm_base}/shm.mem/{interface.component.name}.{interface._id}" queue_type = "shm.mem"
case EthInterface(): case EthInterface():
return f"{self._env._shm_base}/shm.eth/{interface.component.name}.{interface._id}" queue_type = "shm.eth"
case _: case _:
raise Exception("cannot create socket path for given interface type") raise Exception("cannot create socket path for given interface type")
def _is_liste_nor_connect(self, interface: system.base.Interface) -> SockType: assert queue_type is not None
# We use the channel to determine if the socket is a listening or connecting socket. return f"{self._env._shm_base}/{queue_type}/{queue_ident}"
# We perform lookup by using the channel as the channel is unique for both
# interfaces connected to it.
if not interface.is_connected():
raise Exception(
"cannot determine the socket type to use for an interface that isn't connected to a channel"
)
chan = interface.channel
if not chan in self._socket_tracker:
self._socket_tracker[chan] = SockType.LISTEN
return SockType.LISTEN
ty = self._socket_tracker[chan] def _create_opposing_socket(self, socket: Socket) -> Socket:
if ty == SockType.LISTEN: new_ty = (
return SockType.CONNECT SockType.LISTEN if socket._type == SockType.CONNECT else SockType.LISTEN
else: )
return SockType.LISTEN new_path = socket._path
new_socket = Socket(path=new_path, ty=new_ty)
return new_socket
def get_socket(self, interface: system.base.Interface) -> Socket: def get_socket(self, interface: system.base.Interface) -> Socket:
# The other side already created a socket, we just create the opposing
# determine socket type that is needed # side (i.e. connect or listening depending on the already created type)
sock_type = self._is_liste_nor_connect(interface=interface) # and return
socket = self._get_socket_by_interface(interface=interface)
# generate socket path if socket is not None:
new_socket = self._create_opposing_socket(socket=socket)
# neither connecting nor listening side already created a socket, thus we
# create a completely new 'CONNECT' socket
sock_type = SockType.CONNECT
# create the socket path
sock_path = self._interface_to_sock_path(interface=interface) sock_path = self._interface_to_sock_path(interface=interface)
new_socket = Socket(path=sock_path, ty=sock_type)
# update the socket tracker mapping for other side
self._updated_tracker_mapping(interface=interface, socket=new_socket)
return Socket(sock_path, sock_type) return new_socket
# TODO: add more methods constructing paths as required by methods in simulators or image handling classes # TODO: add more methods constructing paths as required by methods in simulators or image handling classes
......
...@@ -26,21 +26,20 @@ import typing as tp ...@@ -26,21 +26,20 @@ import typing as tp
import simbricks.orchestration.system as sys_conf import simbricks.orchestration.system as sys_conf
from simbricks.orchestration.experiment import experiment_environment_new as exp_env from simbricks.orchestration.experiment import experiment_environment_new as exp_env
from simbricks.orchestration.instantiation import base as inst_base from simbricks.orchestration.instantiation import base as inst_base
if tp.TYPE_CHECKING:
from simbricks.orchestration.simulation import (
Channel, HostSim, PCIDevSim, NetSim
)
if tp.TYPE_CHECKING:
from simbricks.orchestration.simulation import Channel, HostSim, PCIDevSim, NetSim
class Simulator(abc.ABC): class Simulator(abc.ABC):
"""Base class for all simulators.""" """Base class for all simulators."""
def __init__(self, e: Simulation) -> None: def __init__(self, e: Simulation, relative_executable_path: str = "") -> None:
self.extra_deps: list[Simulator] = [] self.extra_deps: list[Simulator] = []
self.name = "" self.name = ""
self.experiment = e self.experiment = e
self._components: set[sys_conf.Component] = [] self._components: set[sys_conf.Component] = []
self._relative_executable_path: str = relative_executable_path
@staticmethod @staticmethod
def filter_sockets( def filter_sockets(
...@@ -88,10 +87,11 @@ class Simulator(abc.ABC): ...@@ -88,10 +87,11 @@ class Simulator(abc.ABC):
return [] return []
# TODO: call this in subclasses # TODO: call this in subclasses
def _add_component(self, comp: sys_conf.Channel) -> None: def _add_component(self, comp: sys_base.Component) -> None:
if comp in self._components: if comp in self._components:
raise Exception("cannot add the same specification twice to a simulator") raise Exception("cannot add the same specification twice to a simulator")
self._components.add(comp) self._components.add(comp)
self.experiment.add_spec_sim_map(comp, self)
def _chan_needs_instance(self, chan: sys_conf.Channel) -> bool: def _chan_needs_instance(self, chan: sys_conf.Channel) -> bool:
if ( if (
...@@ -115,6 +115,7 @@ class Simulator(abc.ABC): ...@@ -115,6 +115,7 @@ class Simulator(abc.ABC):
return interface return interface
# TODO: change method to take interface
def _get_socket_and_chan( def _get_socket_and_chan(
self, inst: inst_base.Instantiation, chan: sys_conf.Channel self, inst: inst_base.Instantiation, chan: sys_conf.Channel
) -> tuple[sys_conf.Channel, inst_base.Socket] | tuple[None, None]: ) -> tuple[sys_conf.Channel, inst_base.Socket] | tuple[None, None]:
...@@ -131,6 +132,28 @@ class Simulator(abc.ABC): ...@@ -131,6 +132,28 @@ class Simulator(abc.ABC):
return (channel, socket) return (channel, socket)
def _get_channels_and_sockets(
self, inst: inst_base.Instantiation
) -> tuple[list[sim_chan.Channel], list[inst_base.Socket]]:
channels = []
sockets = []
for comp_spec in self._components:
# TODO: use interfaces() method instead of channels
for chan in comp_spec.channels():
channel, socket = self._get_socket_and_chan(inst=inst, chan=chan)
if channel is None or socket is None:
continue
channels.append(channel)
sockets.append(socket)
return channels, sockets
# pylint: disable=unused-argument # pylint: disable=unused-argument
@abc.abstractmethod @abc.abstractmethod
def run_cmd(self, env: exp_env.ExpEnv) -> str: def run_cmd(self, env: exp_env.ExpEnv) -> str:
...@@ -212,9 +235,7 @@ class Simulation(object): ...@@ -212,9 +235,7 @@ class Simulation(object):
def is_channel_instantiated(self, chan: Channel) -> bool: def is_channel_instantiated(self, chan: Channel) -> bool:
return chan in self._chan_map return chan in self._chan_map
def retrieve_or_create_channel( def retrieve_or_create_channel(self, chan: sys_conf.Channel) -> Channel:
self, chan: sys_conf.Channel
) -> Channel:
if self.is_channel_instantiated(chan): if self.is_channel_instantiated(chan):
return self._chan_map[chan] return self._chan_map[chan]
......
...@@ -31,8 +31,8 @@ from simbricks.orchestration.instantiation import base as inst_base ...@@ -31,8 +31,8 @@ from simbricks.orchestration.instantiation import base as inst_base
class NetSim(base.Simulator): class NetSim(base.Simulator):
"""Base class for network simulators.""" """Base class for network simulators."""
def __init__(self, e: exp.Experiment) -> None: def __init__(self, e: exp.Experiment, relative_executable_path: str = "") -> None:
super().__init__(e) super().__init__(e, relative_executable_path=relative_executable_path)
# TODO: do we want them here? # TODO: do we want them here?
self._switch_specs = [] self._switch_specs = []
self._host_specs = [] self._host_specs = []
...@@ -68,10 +68,52 @@ class NetSim(base.Simulator): ...@@ -68,10 +68,52 @@ class NetSim(base.Simulator):
return [] return []
class SwitchNet(NetSim): class WireNet(NetSim):
def __init__(self, e: exp.Experiment) -> None: def __init__(self, e: exp.Experiment) -> None:
super().__init__(e) super().__init__(e, relative_executable_path="/sims/net/wire/net_wire")
# TODO: probably we want to store these in a common base class...
self._wire_comp: eth.EthWire | None = None
def add_wire(self, wire: eth.EthWire):
assert self._wire_comp is None
super()._add_component(wire)
self._wire_comp = wire
def run_cmd(self, inst: inst_base.Instantiation) -> str:
eth_latency = None
sync_period = None
run_sync = False
channels, sockets = self._get_channels_and_sockets(inst=inst)
assert len(sockets) == 2
for channel in channels:
sync_period = min(sync_period, channel.sync_period)
run_sync = run_sync or channel._synchronized
if (
channel.sys_channel.eth_latency != eth_latency
and eth_latency is not None
):
raise Exception("non unique eth latency")
eth_latency = channel.sys_channel.eth_latency
assert sync_period is not None
assert eth_latency is not None
cmd = inst.join_repo_base(self._relative_executable_path)
cmd += f"{sockets[0]} {sockets[1]} {run_sync} {sync_period} {eth_latency}"
# TODO
if len(env.pcap_file) > 0:
cmd += " " + env.pcap_file
return cmd
class SwitchNet(NetSim):
def __init__(
self, e: exp.Experiment, relative_executable_path="/sims/net/switch/net_switch"
) -> None:
super().__init__(e, relative_executable_path=relative_executable_path)
# TODO: probably we want to store these in a common base class... # TODO: probably we want to store these in a common base class...
self._switch_spec: eth.EthSwitch | None = None self._switch_spec: eth.EthSwitch | None = None
...@@ -79,31 +121,28 @@ class SwitchNet(NetSim): ...@@ -79,31 +121,28 @@ class SwitchNet(NetSim):
assert self._switch_spec is None assert self._switch_spec is None
super()._add_component(switch_spec) super()._add_component(switch_spec)
self._switch_spec = switch_spec self._switch_spec = switch_spec
self.experimente.add_spec_sim_map(self._switch_spec, self)
def run_cmd(self, inst: inst_base.Instantiation) -> str: def run_cmd(self, inst: inst_base.Instantiation) -> str:
assert self._switch_spec is not None assert self._switch_spec is not None
eth_latency = self._switch_spec.channels()[0].latency eth_latency = None
if any(lat != eth_latency for chan in self._switch_spec.channels()):
raise Exception("SwitchNet currently only supports single eth latency")
sync_period = None sync_period = None
run_sync = False run_sync = False
sockets: list[inst_base.Socket] = [] channels, sockets = self._get_channels_and_sockets(inst=inst)
for chan in self._switch_spec.channels(): for channel in channels:
channel, socket = self._get_socket_and_chan(inst=inst, chan=chan)
if channel is None or socket is None:
continue
sync_period = min(sync_period, channel.sync_period) sync_period = min(sync_period, channel.sync_period)
run_sync = run_sync or channel._synchronized run_sync = run_sync or channel._synchronized
sock_paths.append(socket) if (
channel.sys_channel.eth_latency != eth_latency
and eth_latency is not None
):
raise Exception("non unique eth latency")
eth_latency = channel.sys_channel.eth_latency
assert sync_period is not None assert sync_period is not None
assert eth_latency is not None assert eth_latency is not None
cmd = inst.join_repo_base("/sims/net/switch/net_switch") cmd = inst.join_repo_base(self._relative_executable_path)
cmd += f" -S {sync_period} -E {eth_latency}" cmd += f" -S {sync_period} -E {eth_latency}"
if not run_sync: if not run_sync:
...@@ -132,3 +171,22 @@ class SwitchNet(NetSim): ...@@ -132,3 +171,22 @@ class SwitchNet(NetSim):
cleanup.append(s) cleanup.append(s)
cleanup.append(s + "-shm") cleanup.append(s + "-shm")
return cleanup return cleanup
class MemSwitchNet(SwitchNet):
def __init__(self, e: exp.Experiment) -> None:
super().__init__(e, relative_executable_path="/sims/mem/memswitch/memswitch")
self.sync = True
"""AS_ID,VADDR_START,VADDR_END,MEMNODE_MAC,PHYS_START."""
self.mem_map = []
def run_cmd(self, inst: inst_base.Instantiation) -> str:
cmd = super().run_cmd(inst)
for m in self.mem_map:
cmd += " -m " + f" {m[0]},{m[1]},{m[2]},"
cmd += "".join(reversed(m[3].split(":")))
cmd += f",{m[4]}"
return cmd
...@@ -30,7 +30,7 @@ class EthInterface(base.Interface): ...@@ -30,7 +30,7 @@ class EthInterface(base.Interface):
def connect(self, c: base.Channel) -> None: def connect(self, c: base.Channel) -> None:
# Note AK: a bit ugly, but I think we can't get around a rt check here # Note AK: a bit ugly, but I think we can't get around a rt check here
if not c is isinstance(c, EthChannel): if not c is isinstance(c, EthChannel):
raise TypeError('EthInterface only connects to EthChannel') raise TypeError("EthInterface only connects to EthChannel")
super().connect(c) super().connect(c)
...@@ -45,10 +45,28 @@ class EthSimpleNIC(base.Component): ...@@ -45,10 +45,28 @@ class EthSimpleNIC(base.Component):
self.eth_if = EthInterface() self.eth_if = EthInterface()
class EthSwitch(base.Component): class BaseEthNetComponent(base.Component):
def __init__(self, s: base.System) -> None: def __init__(self, s: base.System) -> None:
super().__init__(s) super().__init__(s)
self.eth_ifs: list[EthInterface] = [] self.eth_ifs: EthInterface = []
def if_add(self, i: EthInterface) -> None: def if_add(self, i: EthInterface) -> None:
self.eth_ifs.append(i) self.eth_ifs.append(i)
\ No newline at end of file
def interfaces(self) -> list[Interface]:
return self.eth_ifs
class EthWire(BaseEthNetComponent):
def __init__(self, s: base.System) -> None:
super().__init__(s)
def if_add(self, i: EthInterface) -> None:
if len(self.eth_ifs) > 2:
raise Exception("one can only add 2 interfaces to a EthWire")
self.eth_ifs.append(i)
class EthSwitch(BaseEthNetComponent):
def __init__(self, s: base.System) -> None:
super().__init__(s)
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