You need to sign in or sign up before continuing.
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:
workdir: str = pathlib.Path(),
cpdir: str = pathlib.Path(),
shm_base: str = pathlib.Path(),
output_base: str = pathlib.Path(),
):
# TODO: add more parameters that wont change during instantiation
self._repodir: str = pathlib.Path(repo_path).absolute()
self._workdir: str = pathlib.Path(workdir).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:
......@@ -61,7 +63,11 @@ class Instantiation:
):
self._simulation = simulation
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
def is_absolute_exists(path: str) -> bool:
......@@ -109,27 +115,37 @@ class Instantiation:
def _get_socket_by_channel(
self, channel: simualtion.channel.Channel
) -> Socket | None:
if not channel in self._socket_tracker:
if not channel in self._socket_per_channel:
return None
return self._socket_tracker[channel]
return self._socket_per_channel[channel]
def _updated_tracker_mapping(
self, interface: system.base.Interface, socket: Socket
) -> None:
# update channel mapping
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
if channel not in self._socket_per_channel:
self._socket_per_channel[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
) -> Socket | None:
channel = self._get_chan_by_interface(interface=interface)
socket = self._get_socket_by_channel(channel=channel)
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:
basepath = pathlib.Path(self._env._workdir)
......@@ -160,12 +176,16 @@ class Instantiation:
return new_socket
def get_socket(self, interface: system.base.Interface) -> Socket:
# The other side already created a socket, we just create the opposing
# side (i.e. connect or listening depending on the already created type)
# and return
# check if already a socket is associated with this 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:
new_socket = self._create_opposing_socket(socket=socket)
self._updated_tracker_mapping(interface=interface, socket=new_socket)
return new_socket
# neither connecting nor listening side already created a socket, thus we
......@@ -180,13 +200,21 @@ class Instantiation:
# 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:
path = pathlib.Path(self._env._repodir)
def _join_paths(self, base: str = "", relative_path: str = "") -> str:
path = pathlib.Path(base)
path.joinpath(relative_path)
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()
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:
if Instantiation.is_absolute_exists(hd_name_or_path):
return hd_name_or_path
......
......@@ -94,7 +94,6 @@ class Simulator(abc.ABC):
"""Commands to prepare execution of this simulator."""
return []
# TODO: call this in subclasses
def _add_component(self, comp: sys_base.Component) -> None:
if comp in self._components:
raise Exception("cannot add the same specification twice to a simulator")
......@@ -115,29 +114,29 @@ class Simulator(abc.ABC):
if inter.component in self._components:
assert interface is None
interface = inter
if interface is None:
raise Exception(
"unable to find channel interface for simulators specification"
)
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(
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]:
# 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):
return None, None
# create channel simualtion object
channel = self.experiment.retrieve_or_create_channel(chan)
# create the socket to listen on or connect to
my_interface = self._get_my_interface(chan)
socket = inst.get_socket(my_interface)
socket = inst.get_socket(interface=interface)
return (channel, socket)
def _get_channels_and_sockets(
......@@ -148,12 +147,11 @@ class Simulator(abc.ABC):
sockets = []
for comp_spec in self._components:
for interface in comp_spec.interfaces():
# TODO: use interfaces() method instead of channels
for chan in comp_spec.channels():
channel, socket = self._get_socket_and_chan(inst=inst, chan=chan)
channel, socket = self._get_socket_and_chan(
inst=inst, interface=interface
)
if channel is None or socket is None:
continue
......@@ -172,10 +170,17 @@ class Simulator(abc.ABC):
"""Other simulators to execute before this one."""
return []
# Sockets to be cleaned up
# Sockets to be cleaned up: always the CONNECTING sockets
# pylint: disable=unused-argument
def sockets_cleanup(self, env: exp_env.ExpEnv) -> list[str]:
return []
def sockets_cleanup(self, inst: inst_base.Instantiation) -> list[inst_base.Socket]:
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
# pylint: disable=unused-argument
......
......@@ -49,10 +49,6 @@ class NetSim(base.Simulator):
deps.append(n.net[0].sim)
return deps
# TODO
def sockets_cleanup(self, env: exp_env.ExpEnv) -> list[str]:
pass
# TODO
def sockets_wait(self, env: exp_env.ExpEnv) -> list[str]:
pass
......@@ -73,10 +69,13 @@ class WireNet(NetSim):
def __init__(self, simulation: base.Simulation) -> None:
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...
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):
assert self._wire_comp is None
......@@ -105,9 +104,11 @@ class WireNet(NetSim):
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
if self._relative_pcap_file_path is not None:
pcap_file = inst.join_output_base(
relative_path=self._relative_pcap_file_path
)
cmd += " " + pcap_file
return cmd
......@@ -117,12 +118,14 @@ class SwitchNet(NetSim):
self,
simulation: base.Simulation,
relative_executable_path="/sims/net/switch/net_switch",
relative_pcap_file_path=None,
) -> None:
super().__init__(
simulation=simulation, relative_executable_path=relative_executable_path
)
# TODO: probably we want to store these in a common base class...
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):
assert self._switch_spec is None
......@@ -155,9 +158,11 @@ class SwitchNet(NetSim):
if not run_sync:
cmd += " -u"
# TODO: pcap_file --> no env!!!
if len(env.pcap_file) > 0:
cmd += " -p " + env.pcap_file
if self._relative_pcap_file_path is not None:
pcap_file = inst.join_output_base(
relative_path=self._relative_pcap_file_path
)
cmd += " " + pcap_file
listen, connect = base.Simulator.split_sockets_by_type(sockets)
......@@ -169,23 +174,16 @@ class SwitchNet(NetSim):
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):
def __init__(self, simulation: base.Simulation) -> None:
def __init__(
self, simulation: base.Simulation, relative_pcap_file_path=None
) -> None:
super().__init__(
simulation=simulation,
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."""
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