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

updated socket handling to retrieve sockets at later stage + pcap argument for...

updated socket handling to retrieve sockets at later stage + pcap argument for some network simulators
parent af34751b
...@@ -46,12 +46,14 @@ class InstantiationEnvironment: ...@@ -46,12 +46,14 @@ class InstantiationEnvironment:
workdir: str = pathlib.Path(), workdir: str = pathlib.Path(),
cpdir: str = pathlib.Path(), cpdir: str = pathlib.Path(),
shm_base: str = pathlib.Path(), shm_base: str = pathlib.Path(),
output_base: str = pathlib.Path(),
): ):
# TODO: add more parameters that wont change during instantiation # TODO: add more parameters that wont change during instantiation
self._repodir: str = pathlib.Path(repo_path).absolute() self._repodir: str = pathlib.Path(repo_path).absolute()
self._workdir: str = pathlib.Path(workdir).absolute() self._workdir: str = pathlib.Path(workdir).absolute()
self._cpdir: str = pathlib.Path(cpdir).absolute() self._cpdir: str = pathlib.Path(cpdir).absolute()
self._shm_base = pathlib.Path(workdir).joinpath(shm_base).absolute() self._shm_base: str = pathlib.Path(workdir).joinpath(shm_base).absolute()
self._output_base: str = pathlib.Path(workdir).joinpath(output_base).absolute()
class Instantiation: class Instantiation:
...@@ -61,7 +63,11 @@ class Instantiation: ...@@ -61,7 +63,11 @@ class Instantiation:
): ):
self._simulation = simulation self._simulation = simulation
self._env: InstantiationEnvironment = env self._env: InstantiationEnvironment = env
self._socket_tracker: dict[simulation.channel.Channel, Socket] = {} # we track sockets per channel and interface:
# - per channel tracking is for ensuring that listening as well as connecting simulator use the same socket path
# - per interface mapping is helpful for simulators to access their particular socket objects for cleanup and there alike
self._socket_per_channel: dict[simulation.channel.Channel, Socket] = {}
self._socket_per_interface: dict[system.base.Interface, Socket] = {}
@staticmethod @staticmethod
def is_absolute_exists(path: str) -> bool: def is_absolute_exists(path: str) -> bool:
...@@ -109,27 +115,37 @@ class Instantiation: ...@@ -109,27 +115,37 @@ class Instantiation:
def _get_socket_by_channel( def _get_socket_by_channel(
self, channel: simualtion.channel.Channel self, channel: simualtion.channel.Channel
) -> Socket | None: ) -> Socket | None:
if not channel in self._socket_tracker: if not channel in self._socket_per_channel:
return None return None
return self._socket_tracker[channel] return self._socket_per_channel[channel]
def _updated_tracker_mapping( def _updated_tracker_mapping(
self, interface: system.base.Interface, socket: Socket self, interface: system.base.Interface, socket: Socket
) -> None: ) -> None:
# update channel mapping
channel = self._get_chan_by_interface(interface=interface) channel = self._get_chan_by_interface(interface=interface)
if channel in self._socket_tracker: if channel not in self._socket_per_channel:
raise Exception( self._socket_per_channel[channel] = socket
"cannot update socket tracker mapping, channel is already mapped"
)
self._socket_tracker[channel] = socket
def _get_socket_by_interface( # update interface mapping
if interface in self._socket_per_interface:
raise Exception("an interface cannot be associated with two sockets")
self._socket_per_interface[interface] = socket
def _get_opposing_socket_by_interface(
self, interface: system.base.Interface self, interface: system.base.Interface
) -> Socket | None: ) -> Socket | None:
channel = self._get_chan_by_interface(interface=interface) channel = self._get_chan_by_interface(interface=interface)
socket = self._get_socket_by_channel(channel=channel) socket = self._get_socket_by_channel(channel=channel)
return socket return socket
def _get_socket_by_interface(
self, interface: system.base.Interface
) -> Socket | None:
if interface not in self._socket_per_interface:
return None
return self._socket_per_interface[interface]
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)
...@@ -160,12 +176,16 @@ class Instantiation: ...@@ -160,12 +176,16 @@ class Instantiation:
return new_socket 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 # check if already a socket is associated with this interface
# side (i.e. connect or listening depending on the already created type)
# and return
socket = self._get_socket_by_interface(interface=interface) socket = self._get_socket_by_interface(interface=interface)
if socket is not None:
return socket
# Check if other side already created a socket, and create an opposing one
socket = self._get_opposing_socket_by_interface(interface=interface)
if socket is not None: if socket is not None:
new_socket = self._create_opposing_socket(socket=socket) new_socket = self._create_opposing_socket(socket=socket)
self._updated_tracker_mapping(interface=interface, socket=new_socket)
return new_socket return new_socket
# neither connecting nor listening side already created a socket, thus we # neither connecting nor listening side already created a socket, thus we
...@@ -180,13 +200,21 @@ class Instantiation: ...@@ -180,13 +200,21 @@ class Instantiation:
# 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
def join_repo_base(self, relative_path: str) -> str: def _join_paths(self, base: str = "", relative_path: str = "") -> str:
path = pathlib.Path(self._env._repodir) path = pathlib.Path(base)
path.joinpath(relative_path) path.joinpath(relative_path)
if not path.exists(): if not path.exists():
raise Exception(f"couldn't join {self._env._repodir} and {relative_path}") raise Exception(f"couldn't join {base} and {relative_path}")
return path.absolute() return path.absolute()
def join_repo_base(self, relative_path: str) -> str:
return self._join_paths(base=self._env._repodir, relative_path=relative_path)
def join_output_base(self, relative_path: str) -> str:
return self._join_paths(
base=self._env._output_base, relative_path=relative_path
)
def hd_path(self, hd_name_or_path: str) -> str: def hd_path(self, hd_name_or_path: str) -> str:
if Instantiation.is_absolute_exists(hd_name_or_path): if Instantiation.is_absolute_exists(hd_name_or_path):
return hd_name_or_path return hd_name_or_path
......
...@@ -94,7 +94,6 @@ class Simulator(abc.ABC): ...@@ -94,7 +94,6 @@ class Simulator(abc.ABC):
"""Commands to prepare execution of this simulator.""" """Commands to prepare execution of this simulator."""
return [] return []
# TODO: call this in subclasses
def _add_component(self, comp: sys_base.Component) -> 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")
...@@ -115,29 +114,29 @@ class Simulator(abc.ABC): ...@@ -115,29 +114,29 @@ class Simulator(abc.ABC):
if inter.component in self._components: if inter.component in self._components:
assert interface is None assert interface is None
interface = inter interface = inter
if interface is None: if interface is None:
raise Exception( raise Exception(
"unable to find channel interface for simulators specification" "unable to find channel interface for simulators specification"
) )
return interface return interface
# TODO: change method to take interface def _get_sys_chan(self, interface: sys_conf.Interface) -> sys_conf.Channel:
if not interface.is_connected():
raise Exception("interface does not need a channel as it is not connected")
return interface.channel
def _get_socket_and_chan( def _get_socket_and_chan(
self, inst: inst_base.Instantiation, chan: sys_conf.Channel self, inst: inst_base.Instantiation, interface: sys_conf.Interface
) -> tuple[sys_conf.Channel, inst_base.Socket] | tuple[None, None]: ) -> tuple[sys_conf.Channel, inst_base.Socket] | tuple[None, None]:
# check if this channel is simulator internal, i.e. doesn't need a shared memory queue # get the channel associated with this interface
chan = self._get_sys_chan(interface=interface)
# check if interfaces channel is simulator internal, i.e. doesnt need an instanciation
if not self._chan_needs_instance(chan): if not self._chan_needs_instance(chan):
return None, None return None, None
# create channel simualtion object # create channel simualtion object
channel = self.experiment.retrieve_or_create_channel(chan) channel = self.experiment.retrieve_or_create_channel(chan)
# create the socket to listen on or connect to # create the socket to listen on or connect to
my_interface = self._get_my_interface(chan) socket = inst.get_socket(interface=interface)
socket = inst.get_socket(my_interface)
return (channel, socket) return (channel, socket)
def _get_channels_and_sockets( def _get_channels_and_sockets(
...@@ -148,12 +147,11 @@ class Simulator(abc.ABC): ...@@ -148,12 +147,11 @@ class Simulator(abc.ABC):
sockets = [] sockets = []
for comp_spec in self._components: for comp_spec in self._components:
for interface in comp_spec.interfaces():
# TODO: use interfaces() method instead of channels channel, socket = self._get_socket_and_chan(
for chan in comp_spec.channels(): inst=inst, interface=interface
)
channel, socket = self._get_socket_and_chan(inst=inst, chan=chan)
if channel is None or socket is None: if channel is None or socket is None:
continue continue
...@@ -172,10 +170,17 @@ class Simulator(abc.ABC): ...@@ -172,10 +170,17 @@ class Simulator(abc.ABC):
"""Other simulators to execute before this one.""" """Other simulators to execute before this one."""
return [] return []
# Sockets to be cleaned up # Sockets to be cleaned up: always the CONNECTING sockets
# pylint: disable=unused-argument # pylint: disable=unused-argument
def sockets_cleanup(self, env: exp_env.ExpEnv) -> list[str]: def sockets_cleanup(self, inst: inst_base.Instantiation) -> list[inst_base.Socket]:
return [] sockets = []
for comp_spec in self._components:
for interface in comp_spec.interfaces():
socket = inst.get_socket(interface=interface)
if socket._type == inst_base.SockType.CONNECT:
sockets.append(socket)
return sockets
# sockets to wait for indicating the simulator is ready # sockets to wait for indicating the simulator is ready
# pylint: disable=unused-argument # pylint: disable=unused-argument
......
...@@ -49,10 +49,6 @@ class NetSim(base.Simulator): ...@@ -49,10 +49,6 @@ class NetSim(base.Simulator):
deps.append(n.net[0].sim) deps.append(n.net[0].sim)
return deps return deps
# TODO
def sockets_cleanup(self, env: exp_env.ExpEnv) -> list[str]:
pass
# TODO # TODO
def sockets_wait(self, env: exp_env.ExpEnv) -> list[str]: def sockets_wait(self, env: exp_env.ExpEnv) -> list[str]:
pass pass
...@@ -73,10 +69,13 @@ class WireNet(NetSim): ...@@ -73,10 +69,13 @@ class WireNet(NetSim):
def __init__(self, simulation: base.Simulation) -> None: def __init__(self, simulation: base.Simulation) -> None:
super().__init__( super().__init__(
simulation=simulation, relative_executable_path="/sims/net/wire/net_wire" simulation=simulation,
relative_executable_path="/sims/net/wire/net_wire",
relative_pcap_file_path=None,
) )
# 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._wire_comp: eth.EthWire | None = None self._wire_comp: eth.EthWire | None = None
self._relative_pcap_file_path: str | None = relative_pcap_file_path
def add_wire(self, wire: eth.EthWire): def add_wire(self, wire: eth.EthWire):
assert self._wire_comp is None assert self._wire_comp is None
...@@ -105,9 +104,11 @@ class WireNet(NetSim): ...@@ -105,9 +104,11 @@ class WireNet(NetSim):
cmd = inst.join_repo_base(self._relative_executable_path) cmd = inst.join_repo_base(self._relative_executable_path)
cmd += f"{sockets[0]} {sockets[1]} {run_sync} {sync_period} {eth_latency}" cmd += f"{sockets[0]} {sockets[1]} {run_sync} {sync_period} {eth_latency}"
# TODO if self._relative_pcap_file_path is not None:
if len(env.pcap_file) > 0: pcap_file = inst.join_output_base(
cmd += " " + env.pcap_file relative_path=self._relative_pcap_file_path
)
cmd += " " + pcap_file
return cmd return cmd
...@@ -117,12 +118,14 @@ class SwitchNet(NetSim): ...@@ -117,12 +118,14 @@ class SwitchNet(NetSim):
self, self,
simulation: base.Simulation, simulation: base.Simulation,
relative_executable_path="/sims/net/switch/net_switch", relative_executable_path="/sims/net/switch/net_switch",
relative_pcap_file_path=None,
) -> None: ) -> None:
super().__init__( super().__init__(
simulation=simulation, relative_executable_path=relative_executable_path simulation=simulation, 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
self._relative_pcap_file_path: str | None = relative_pcap_file_path
def add_switch(self, switch_spec: eth.EthSwitch): def add_switch(self, switch_spec: eth.EthSwitch):
assert self._switch_spec is None assert self._switch_spec is None
...@@ -155,9 +158,11 @@ class SwitchNet(NetSim): ...@@ -155,9 +158,11 @@ class SwitchNet(NetSim):
if not run_sync: if not run_sync:
cmd += " -u" cmd += " -u"
# TODO: pcap_file --> no env!!! if self._relative_pcap_file_path is not None:
if len(env.pcap_file) > 0: pcap_file = inst.join_output_base(
cmd += " -p " + env.pcap_file relative_path=self._relative_pcap_file_path
)
cmd += " " + pcap_file
listen, connect = base.Simulator.split_sockets_by_type(sockets) listen, connect = base.Simulator.split_sockets_by_type(sockets)
...@@ -169,23 +174,16 @@ class SwitchNet(NetSim): ...@@ -169,23 +174,16 @@ class SwitchNet(NetSim):
return cmd return cmd
# TODO
def sockets_cleanup(self, env: exp_env.ExpEnv) -> list[str]:
# cleanup here will just have listening eth sockets, switch also creates
# shm regions for each with a "-shm" suffix
cleanup = []
for s in super().sockets_cleanup(env):
cleanup.append(s)
cleanup.append(s + "-shm")
return cleanup
class MemSwitchNet(SwitchNet): class MemSwitchNet(SwitchNet):
def __init__(self, simulation: base.Simulation) -> None: def __init__(
self, simulation: base.Simulation, relative_pcap_file_path=None
) -> None:
super().__init__( super().__init__(
simulation=simulation, simulation=simulation,
relative_executable_path="/sims/mem/memswitch/memswitch", relative_executable_path="/sims/mem/memswitch/memswitch",
relative_pcap_file_path=relative_pcap_file_path,
) )
"""AS_ID,VADDR_START,VADDR_END,MEMNODE_MAC,PHYS_START.""" """AS_ID,VADDR_START,VADDR_END,MEMNODE_MAC,PHYS_START."""
self.mem_map = [] self.mem_map = []
......
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