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

experiments/simbricks/orchestration: started impl draft for instantiation +...

experiments/simbricks/orchestration: started impl draft for instantiation + added example usage along network simulator
parent 342a58b8
......@@ -29,8 +29,6 @@ import simbricks.orchestration.simulation.host as sim_host
import simbricks.orchestration.simulation.channel as sim_channel
import simbricks.orchestration.simulation.pcidev as sim_pcidev
import simbricks.orchestration.system.base as system_base
import simbricks.orchestration.system.host.base as system_host_base
import simbricks.orchestration.system.eth as system_eth
......@@ -40,7 +38,12 @@ import simbricks.orchestration.system.pcie as system_pcie
from simbricks.orchestration.proxy import NetProxyConnecter, NetProxyListener
from simbricks.orchestration.simulators import (
HostSim, I40eMultiNIC, NetSim, NICSim, PCIDevSim, Simulator
HostSim,
I40eMultiNIC,
NetSim,
NICSim,
PCIDevSim,
Simulator,
)
......@@ -86,24 +89,25 @@ class Experiment(object):
self.sys_sim_map: dict[system.Component, simulation.Simulator] = {}
"""System component and its simulator pairs"""
self._chan_map: dict[system.Channel, simulation.channel.Channel] = {}
self._chan_map: dict[system_base.Channel, sim_channel.Channel] = {}
"""Channel spec and its instanciation"""
def add_spec_sim_map(self, sys: system.component, sim: simulation.Simulator):
""" Add a mapping from specification to simulation instance"""
"""Add a mapping from specification to simulation instance"""
if sys in sys_sim_map:
raise Exception("system component is already mapped by simulator")
self.sys_sim_map[sys] = sim
def is_channel_instantiated(self, chan: system.Channel) -> bool:
def is_channel_instantiated(self, chan: system_base.Channel) -> bool:
return chan in self._chan_map
def retrieve_or_create_channel(self, chan: system.Channel) -> simulation.channel.Channel:
def retrieve_or_create_channel(
self, chan: system_base.Channel
) -> sim_channel.Channel:
if self.is_channel_instantiated(chan):
return self._chan_map[chan]
# TODO: pass in specification into channel constructor
channel = simulation.channel.Channel(self)
channel = sim_channel.Channel(self, chan)
self._chan_map[chan] = channel
return channel
......@@ -115,7 +119,7 @@ class Experiment(object):
"""Add a host simulator to the experiment."""
for h in self.hosts:
if h.name == sim.name:
raise ValueError('Duplicate host name')
raise ValueError("Duplicate host name")
self.hosts.append(sim)
def add_nic(self, sim: NICSim | I40eMultiNIC):
......@@ -126,26 +130,26 @@ class Experiment(object):
"""Add a PCIe device simulator to the experiment."""
for d in self.pcidevs:
if d.name == sim.name:
raise ValueError('Duplicate pcidev name')
raise ValueError("Duplicate pcidev name")
self.pcidevs.append(sim)
def add_memdev(self, sim: simulators.MemDevSim):
for d in self.memdevs:
if d.name == sim.name:
raise ValueError('Duplicate memdev name')
raise ValueError("Duplicate memdev name")
self.memdevs.append(sim)
def add_netmem(self, sim: simulators.NetMemSim):
for d in self.netmems:
if d.name == sim.name:
raise ValueError('Duplicate netmems name')
raise ValueError("Duplicate netmems name")
self.netmems.append(sim)
def add_network(self, sim: NetSim) -> None:
"""Add a network simulator to the experiment."""
for n in self.networks:
if n.name == sim.name:
raise ValueError('Duplicate net name')
raise ValueError("Duplicate net name")
self.networks.append(sim)
def all_simulators(self) -> tp.Iterable[Simulator]:
......@@ -167,13 +171,13 @@ class Experiment(object):
for s in self.all_simulators():
cores += s.resreq_cores()
return cores
def find_sim(self, comp: system_base.Component) -> sim_base.Simulator:
"""Returns the used simulator object for the system component."""
for c, sim in self.sys_sim_map.items():
if c == comp:
return sim
raise Exception("Simulator Not Found")
......
# Copyright 2022 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.
from simbricks.orchestration import simulation
import enum
import pathlib
class SockType(enum.Enum):
LISTEN = enum.auto()
CONNECT = enum.auto()
class Socket:
def __init__(self, path: str = "", ty: SockType = SockType.LISTEN):
self._path = path
self._type = ty
class InstantiationEnvironment:
def __init__(
self,
repo_path: str = pathlib.Path(__file__).parents[3].resolve(),
workdir: str = pathlib.Path(),
cpdir: 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()
class Instantiation:
def __init__(
self, simulation, env: InstantiationEnvironment = InstantiationEnvironment()
):
self._simulation = simulation
self._env: InstantiationEnvironment = env
self._socket_tracker: set[tuple[simulation.channel.Channel, SockType]] = set()
def get_socket_path(self, chan: simulation.channel.Channel) -> Socket:
# TODO: use self._socket_tracker to determine socket type that is needed
sock_type = SockType.LISTEN
# TODO: generate socket path
sock_path = ""
return Socket(sock_path, sock_type)
@staticmethod
def is_absolute_exists(path: str) -> bool:
path = pathlib.Path(path)
return path.is_absolute() and path.is_file()
# TODO: add more methods constructing paths as required by methods in simulators or image handling classes
def hd_path(self, hd_name_or_path: str) -> str:
if Instantiation.is_absolute_exists(hd_name_or_path):
return hd_name_or_path
path = pathlib.Path(
f"{self._env._repodir}/images/output-{hd_name_or_path}/{hd_name_or_path}"
)
return path.absolute()
def dynamic_img_path(self, format: str) -> str:
# TODO
return ""
......@@ -20,17 +20,22 @@
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import typing as tp
import abc
import simbricks.orchestration.experiments as exp
from simbricks.orchestration.experiment.experiment_environment_new import ExpEnv
from simbricks.orchestration.system import base as sys_base
from simbricks.orchestration.simulation import channel as sim_chan
from simbricks.orchestration.experiment import experiment_environment_new as exp_env
from simbricks.orchestration.instantiation import base as inst_base
class Simulator(object):
class Simulator(abc.ABC):
"""Base class for all simulators."""
def __init__(self, e: exp.Experiment) -> None:
self.extra_deps: tp.List[Simulator] = []
self.name = ''
self.extra_deps: list[Simulator] = []
self.name = ""
self.experiment = e
self._components: set[sys_base.Component] = []
def resreq_cores(self) -> int:
"""
......@@ -50,34 +55,55 @@ class Simulator(object):
def full_name(self) -> str:
"""Full name of the simulator."""
return ''
return ""
# pylint: disable=unused-argument
def prep_cmds(self, env: ExpEnv) -> tp.List[str]:
def prep_cmds(self, env: exp_env.ExpEnv) -> list[str]:
"""Commands to prepare execution of this simulator."""
return []
# TODO: call this in subclasses
def _add_component(self, comp: sys_base.Channel) -> None:
self._components.add(comp)
def _chan_needs_instance(self, chan: sys_base.Channel) -> bool:
if (
chan.a.component in self._components
and chan.b.component in self._components
):
return False
return True
def _get_sock_path(
self, inst: inst_base.Instantiation, chan: sys_base.Channel
) -> tuple[sim_chan.Channel, inst_base.Socket] | tuple[None, None]:
if not self._chan_needs_instance(chan):
return None, None
channel = self.experiment.retrieve_or_create_channel(chan)
return channel, inst.get_socket_path(channel)
# pylint: disable=unused-argument
def run_cmd(self, env: ExpEnv) -> tp.Optional[str]:
@abc.abstractmethod
def run_cmd(self, env: exp_env.ExpEnv) -> str:
"""Command to execute this simulator."""
return None
return ""
def dependencies(self):
def dependencies(self) -> list[Simulator]:
"""Other simulators to execute before this one."""
return []
# Sockets to be cleaned up
# pylint: disable=unused-argument
def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
def sockets_cleanup(self, env: exp_env.ExpEnv) -> list[str]:
return []
# sockets to wait for indicating the simulator is ready
# pylint: disable=unused-argument
def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
def sockets_wait(self, env: exp_env.ExpEnv) -> list[str]:
return []
def start_delay(self) -> int:
return 5
def wait_terminate(self) -> bool:
return False
\ No newline at end of file
return False
......@@ -27,19 +27,19 @@ import simbricks.orchestration.system.base as system_base
from simbricks.orchestration.experiment.experiment_environment_new import ExpEnv
class Channel(sim_base.Simulator):
def __init__(self, e: exp.Experiment):
def __init__(self, e: exp.Experiment, chan: system_base.Channel):
super().__init__(e)
self.sync_period = 500 # nano second
self.sys_channel: system_base.Channel = None
self._synchronized: bool = True
self.sync_period: int = 500 # nano second
self.sys_channel: system_base.Channel = chan
def full_name(self) -> str:
return 'channel.' + self.name
def add(self, ch: system_base.Channel):
self.sys_channel = ch
self.name = f'{ch.id}'
self.experiment.sys_sim_map[ch] = self
return "channel." + self.name
\ No newline at end of file
# def add(self, ch: system_base.Channel):
# self.sys_channel = ch
# self.name = f"{ch.id}"
# self.experiment.sys_sim_map[ch] = self
# Copyright 2024 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.
\ No newline at end of file
......@@ -20,83 +20,118 @@
# 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.simulation.base as base
import simbricks.orchestration.system as system
import typing as tp
import simbricks.orchestration.experiments as exp
from simbricks.orchestration.experiment.experiment_environment_new import ExpEnv
import abc
import sys
from
from simbricks.orchestration import experiments
from simbricks.orchestration.simulation import base
from simbricks.orchestration.system import eth
from simbricks.orchestration.instantiation import base as inst_base
class NetSim(Simulator):
class NetSim(base.Simulator):
"""Base class for network simulators."""
def __init__(self, e: exp.Experiment) -> None:
super().__init__(e)
self.opt = ''
self.switches: tp.List[spec.Switch] = []
self.nicSim: tp.List[I40eNicSim] = []
self.wait = False
# TODO: do we want them here?
self._switch_specs = []
self._host_specs = []
def full_name(self) -> str:
return 'net.' + self.name
def add(self, switch: spec.Switch):
self.switches.append(switch)
# switch.sim = self
self.experiment.add_network(self)
self.name = f'{switch.id}'
for ndev in switch.netdevs:
self.nicSim.append(n.net[0].sim)
def connect_sockets(self, env: ExpEnv) -> tp.List[tp.Tuple[Simulator, str]]:
sockets = []
for n in self.nicSim:
sockets.append((n, env.nic_eth_path(n)))
return sockets
return "net." + self.name
def dependencies(self) -> tp.List[Simulator]:
def dependencies(self) -> list[base.Simulator]:
# TODO
deps = []
for s in self.switches:
for n in s.netdevs:
deps.append(n.net[0].sim)
return deps
def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
pass
def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
# TODO
def sockets_cleanup(self, env: exp_env.ExpEnv) -> list[str]:
pass
# TODO
def sockets_wait(self, env: exp_env.ExpEnv) -> list[str]:
pass
def wait_terminate(self) -> bool:
# TODO
return self.wait
def init_network(self) -> None:
pass
def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
cleanup = []
return cleanup
def sockets_cleanup(self, env: exp_env.ExpEnv) -> list[str]:
# TODO
return []
class SwitchBMSim(NetSim):
class SwitchNet(NetSim):
def __init__(self, e: exp.Experiment):
def __init__(self, e: exp.Experiment) -> None:
super().__init__(e)
# TODO: probably we want to store these in a common base class...
self._switch_spec: eth.EthSwitch | None = None
def add_switch(self, switch_spec: eth.EthSwitch):
assert self._switch_spec is None
super()._add_component(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:
assert self._switch_spec is not None
eth_latency = self._switch_spec.channels()[0].latency
if any(lat != eth_latency for chan in self._switch_spec.channels()):
raise Exception("SwitchNet currently only supports single eth latency")
sync_period = None
run_sync = False
sockets: list[inst_base.Socket] = []
for chan in self._switch_spec.channels():
channel, socket = self._get_sock_path(inst=inst, chan=chan)
if channel is None or socket is None:
continue
sync_period = min(sync_period, channel.sync_period)
run_sync = run_sync or channel._synchronized
sock_paths.append(socket)
assert sync_period is not None
assert eth_latency is not None
def run_cmd(self, env: ExpEnv) -> str:
cmd = env.repodir + '/sims/net/switch/net_switch'
cmd += f' -S {self.switches[0].sync_period} -E {self.switches[0].eth_latency}'
cmd = env.repodir + "/sims/net/switch/net_switch"
cmd += f" -S {sync_period} -E {eth_latency}"
if not self.switches[0].sync:
cmd += ' -u'
if not run_sync:
cmd += " -u"
if len(env.pcap_file) > 0:
cmd += ' -p ' + env.pcap_file
for (_, n) in self.connect_sockets(env):
cmd += ' -s ' + n
# for (_, n) in self.listen_sockets(env):
# cmd += ' -h ' + n
return cmd
\ No newline at end of file
cmd += " -p " + env.pcap_file
connect = ''
listen = ''
for sock in sockets:
if sock._type == inst_base.SockType.LISTEN:
listen += " -h " + sock._path
else:
connect += " -s " + sock._path
cmd += connect
cmd += listen
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
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