"driver/driver.cpp" did not exist on "63cdc6d2a4d5e15250c0bcb653238eb9da823cd5"
Commit 05bfbefb authored by Jonas Kaufmann's avatar Jonas Kaufmann Committed by Antoine Kaufmann
Browse files

orchestration: add type annotations throughout

parent 85c94472
...@@ -122,7 +122,7 @@ for host_type in host_types: ...@@ -122,7 +122,7 @@ for host_type in host_types:
e.assign_sim_host(c.pcidevs[0], k) e.assign_sim_host(c.pcidevs[0], k)
if k != 0: if k != 0:
cp.add_nic(c.pcidevs[0]) cp.add_nic(c.nics[0])
k = (k + 1) % 2 k = (k + 1) % 2
# add to experiments # add to experiments
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import abc
import asyncio import asyncio
import os import os
import pathlib import pathlib
...@@ -35,9 +36,9 @@ class Component(object): ...@@ -35,9 +36,9 @@ class Component(object):
def __init__(self, cmd_parts: tp.List[str], with_stdin=False): def __init__(self, cmd_parts: tp.List[str], with_stdin=False):
self.is_ready = False self.is_ready = False
self.stdout = [] self.stdout: tp.List[str] = []
self.stdout_buf = bytearray() self.stdout_buf = bytearray()
self.stderr = [] self.stderr: tp.List[str] = []
self.stderr_buf = bytearray() self.stderr_buf = bytearray()
self.cmd_parts = cmd_parts self.cmd_parts = cmd_parts
#print(cmd_parts) #print(cmd_parts)
...@@ -46,7 +47,7 @@ class Component(object): ...@@ -46,7 +47,7 @@ class Component(object):
self._proc: Process self._proc: Process
self._terminate_future: asyncio.Task self._terminate_future: asyncio.Task
def _parse_buf(self, buf, data): def _parse_buf(self, buf: bytearray, data: bytes) -> tp.List[str]:
if data is not None: if data is not None:
buf.extend(data) buf.extend(data)
lines = [] lines = []
...@@ -62,14 +63,14 @@ class Component(object): ...@@ -62,14 +63,14 @@ class Component(object):
lines.append(buf.decode('utf-8')) lines.append(buf.decode('utf-8'))
return lines return lines
async def _consume_out(self, data: bytes): async def _consume_out(self, data: bytes) -> None:
eof = len(data) == 0 eof = len(data) == 0
ls = self._parse_buf(self.stdout_buf, data) ls = self._parse_buf(self.stdout_buf, data)
if len(ls) > 0 or eof: if len(ls) > 0 or eof:
await self.process_out(ls, eof=eof) await self.process_out(ls, eof=eof)
self.stdout.extend(ls) self.stdout.extend(ls)
async def _consume_err(self, data: bytes): async def _consume_err(self, data: bytes) -> None:
eof = len(data) == 0 eof = len(data) == 0
ls = self._parse_buf(self.stderr_buf, data) ls = self._parse_buf(self.stderr_buf, data)
if len(ls) > 0 or eof: if len(ls) > 0 or eof:
...@@ -85,7 +86,7 @@ class Component(object): ...@@ -85,7 +86,7 @@ class Component(object):
await fn(bs) await fn(bs)
return return
async def _waiter(self): async def _waiter(self) -> None:
stdout_handler = asyncio.create_task( stdout_handler = asyncio.create_task(
self._read_stream(self._proc.stdout, self._consume_out) self._read_stream(self._proc.stdout, self._consume_out)
) )
...@@ -96,12 +97,12 @@ class Component(object): ...@@ -96,12 +97,12 @@ class Component(object):
await asyncio.wait([stdout_handler, stderr_handler]) await asyncio.wait([stdout_handler, stderr_handler])
await self.terminated(rc) await self.terminated(rc)
async def send_input(self, bs, eof=False): async def send_input(self, bs: bytes, eof=False) -> None:
self._proc.stdin.write(bs) self._proc.stdin.write(bs)
if eof: if eof:
self._proc.stdin.close() self._proc.stdin.close()
async def start(self): async def start(self) -> None:
if self.with_stdin: if self.with_stdin:
stdin = asyncio.subprocess.PIPE stdin = asyncio.subprocess.PIPE
else: else:
...@@ -116,7 +117,7 @@ class Component(object): ...@@ -116,7 +117,7 @@ class Component(object):
self._terminate_future = asyncio.create_task(self._waiter()) self._terminate_future = asyncio.create_task(self._waiter())
await self.started() await self.started()
async def wait(self): async def wait(self) -> None:
""" """
Wait for running process to finish and output to be collected. Wait for running process to finish and output to be collected.
...@@ -125,22 +126,22 @@ class Component(object): ...@@ -125,22 +126,22 @@ class Component(object):
""" """
await asyncio.shield(self._terminate_future) await asyncio.shield(self._terminate_future)
async def interrupt(self): async def interrupt(self) -> None:
"""Sends an interrupt signal.""" """Sends an interrupt signal."""
if self._proc.returncode is None: if self._proc.returncode is None:
self._proc.send_signal(signal.SIGINT) self._proc.send_signal(signal.SIGINT)
async def terminate(self): async def terminate(self) -> None:
"""Sends a terminate signal.""" """Sends a terminate signal."""
if self._proc.returncode is None: if self._proc.returncode is None:
self._proc.terminate() self._proc.terminate()
async def kill(self): async def kill(self) -> None:
"""Sends a kill signal.""" """Sends a kill signal."""
if self._proc.returncode is None: if self._proc.returncode is None:
self._proc.kill() self._proc.kill()
async def int_term_kill(self, delay=5): async def int_term_kill(self, delay: int = 5) -> None:
"""Attempts to stop this component by sending signals in the following """Attempts to stop this component by sending signals in the following
order: interrupt, terminate, kill.""" order: interrupt, terminate, kill."""
await self.interrupt() await self.interrupt()
...@@ -168,41 +169,47 @@ class Component(object): ...@@ -168,41 +169,47 @@ class Component(object):
await self._proc.wait() await self._proc.wait()
async def started(self): async def started(self) -> None:
pass pass
async def terminated(self, rc): async def terminated(self, rc) -> None:
pass pass
async def process_out(self, lines, eof): async def process_out(self, lines: tp.List[str], eof: bool) -> None:
pass pass
async def process_err(self, lines, eof): async def process_err(self, lines: tp.List[str], eof: bool) -> None:
pass pass
class SimpleComponent(Component): class SimpleComponent(Component):
def __init__( def __init__(
self, label, cmd_parts, *args, verbose=True, canfail=False, **kwargs self,
): label: str,
cmd_parts: tp.List[str],
*args,
verbose=True,
canfail=False,
**kwargs
) -> None:
self.label = label self.label = label
self.verbose = verbose self.verbose = verbose
self.canfail = canfail self.canfail = canfail
self.cmd_parts = cmd_parts self.cmd_parts = cmd_parts
super().__init__(cmd_parts, *args, **kwargs) super().__init__(cmd_parts, *args, **kwargs)
async def process_out(self, lines, eof): async def process_out(self, lines: tp.List[str], eof: bool) -> None:
if self.verbose: if self.verbose:
for _ in lines: for _ in lines:
print(self.label, 'OUT:', lines, flush=True) print(self.label, 'OUT:', lines, flush=True)
async def process_err(self, lines, eof): async def process_err(self, lines: tp.List[str], eof: bool) -> None:
if self.verbose: if self.verbose:
for _ in lines: for _ in lines:
print(self.label, 'ERR:', lines, flush=True) print(self.label, 'ERR:', lines, flush=True)
async def terminated(self, rc): async def terminated(self, rc: int) -> None:
if self.verbose: if self.verbose:
print(self.label, 'TERMINATED:', rc, flush=True) print(self.label, 'TERMINATED:', rc, flush=True)
if not self.canfail and rc != 0: if not self.canfail and rc != 0:
...@@ -213,14 +220,14 @@ class SimpleRemoteComponent(SimpleComponent): ...@@ -213,14 +220,14 @@ class SimpleRemoteComponent(SimpleComponent):
def __init__( def __init__(
self, self,
host_name, host_name: str,
label, label: str,
cmd_parts, cmd_parts: tp.List[str],
*args, *args,
cwd=None, cwd: tp.Optional[str] = None,
ssh_extra_args=None, ssh_extra_args: tp.Optional[tp.List[str]] = None,
**kwargs **kwargs
): ) -> None:
if ssh_extra_args is None: if ssh_extra_args is None:
ssh_extra_args = [] ssh_extra_args = []
...@@ -246,7 +253,7 @@ class SimpleRemoteComponent(SimpleComponent): ...@@ -246,7 +253,7 @@ class SimpleRemoteComponent(SimpleComponent):
self._pid_fut: tp.Optional[asyncio.Future] = None self._pid_fut: tp.Optional[asyncio.Future] = None
def _ssh_cmd(self, parts): def _ssh_cmd(self, parts: tp.List[str]) -> tp.List[str]:
"""SSH invocation of command for this host.""" """SSH invocation of command for this host."""
return [ return [
'ssh', 'ssh',
...@@ -256,13 +263,13 @@ class SimpleRemoteComponent(SimpleComponent): ...@@ -256,13 +263,13 @@ class SimpleRemoteComponent(SimpleComponent):
'StrictHostKeyChecking=no' 'StrictHostKeyChecking=no'
] + self.extra_flags + [self.host_name, '--'] + parts ] + self.extra_flags + [self.host_name, '--'] + parts
async def start(self): async def start(self) -> None:
"""Start this command (includes waiting for its pid).""" """Start this command (includes waiting for its pid)."""
self._pid_fut = asyncio.get_running_loop().create_future() self._pid_fut = asyncio.get_running_loop().create_future()
await super().start() await super().start()
await self._pid_fut await self._pid_fut
async def process_out(self, lines, eof): async def process_out(self, lines: tp.List[str], eof: bool) -> None:
"""Scans output and set PID future once PID line found.""" """Scans output and set PID future once PID line found."""
if not self._pid_fut.done(): if not self._pid_fut.done():
newlines = [] newlines = []
...@@ -282,7 +289,7 @@ class SimpleRemoteComponent(SimpleComponent): ...@@ -282,7 +289,7 @@ class SimpleRemoteComponent(SimpleComponent):
self._pid_fut.cancel() self._pid_fut.cancel()
await super().process_out(lines, eof) await super().process_out(lines, eof)
async def _kill_cmd(self, sig): async def _kill_cmd(self, sig: str) -> None:
"""Send signal to command by running ssh kill -$sig $PID.""" """Send signal to command by running ssh kill -$sig $PID."""
cmd_parts = self._ssh_cmd([ cmd_parts = self._ssh_cmd([
'kill', '-' + sig, str(self._pid_fut.result()) 'kill', '-' + sig, str(self._pid_fut.result())
...@@ -290,43 +297,47 @@ class SimpleRemoteComponent(SimpleComponent): ...@@ -290,43 +297,47 @@ class SimpleRemoteComponent(SimpleComponent):
proc = await asyncio.create_subprocess_exec(*cmd_parts) proc = await asyncio.create_subprocess_exec(*cmd_parts)
await proc.wait() await proc.wait()
async def interrupt(self): async def interrupt(self) -> None:
await self._kill_cmd('INT') await self._kill_cmd('INT')
async def terminate(self): async def terminate(self) -> None:
await self._kill_cmd('TERM') await self._kill_cmd('TERM')
async def kill(self): async def kill(self) -> None:
await self._kill_cmd('KILL') await self._kill_cmd('KILL')
class Executor(abc.ABC): class Executor(abc.ABC):
def __init__(self): def __init__(self) -> None:
self.ip = None self.ip = None
@abc.abstractmethod @abc.abstractmethod
def create_component(self, label, parts, **kwargs) -> SimpleComponent: def create_component(
self, label: str, parts: tp.List[str], **kwargs
) -> SimpleComponent:
pass pass
@abc.abstractmethod @abc.abstractmethod
async def await_file(self, path, delay=0.05, verbose=False) -> None: async def await_file(self, path: str, delay=0.05, verbose=False) -> None:
pass pass
@abc.abstractmethod @abc.abstractmethod
async def send_file(self, path, verbose=False) -> None: async def send_file(self, path: str, verbose=False) -> None:
pass pass
@abc.abstractmethod @abc.abstractmethod
async def mkdir(self, path, verbose=False) -> None: async def mkdir(self, path: str, verbose=False) -> None:
pass pass
@abc.abstractmethod @abc.abstractmethod
async def rmtree(self, path, verbose=False) -> None: async def rmtree(self, path: str, verbose=False) -> None:
pass pass
# runs the list of commands as strings sequentially # runs the list of commands as strings sequentially
async def run_cmdlist(self, label, cmds, verbose=True): async def run_cmdlist(
self, label: str, cmds: tp.List[str], verbose=True
) -> None:
i = 0 i = 0
for cmd in cmds: for cmd in cmds:
cmd_c = self.create_component( cmd_c = self.create_component(
...@@ -335,7 +346,7 @@ class Executor(abc.ABC): ...@@ -335,7 +346,7 @@ class Executor(abc.ABC):
await cmd_c.start() await cmd_c.start()
await cmd_c.wait() await cmd_c.wait()
async def await_files(self, paths, *args, **kwargs): async def await_files(self, paths: tp.List[str], *args, **kwargs) -> None:
xs = [] xs = []
for p in paths: for p in paths:
waiter = asyncio.create_task(self.await_file(p, *args, **kwargs)) waiter = asyncio.create_task(self.await_file(p, *args, **kwargs))
...@@ -346,10 +357,14 @@ class Executor(abc.ABC): ...@@ -346,10 +357,14 @@ class Executor(abc.ABC):
class LocalExecutor(Executor): class LocalExecutor(Executor):
def create_component(self, label, parts, **kwargs): def create_component(
self, label: str, parts: tp.List[str], **kwargs
) -> SimpleComponent:
return SimpleComponent(label, parts, **kwargs) return SimpleComponent(label, parts, **kwargs)
async def await_file(self, path, delay=0.05, verbose=False, timeout=30): async def await_file(
self, path: str, delay=0.05, verbose=False, timeout=30
) -> None:
if verbose: if verbose:
print(f'await_file({path})') print(f'await_file({path})')
t = 0 t = 0
...@@ -359,14 +374,14 @@ class LocalExecutor(Executor): ...@@ -359,14 +374,14 @@ class LocalExecutor(Executor):
await asyncio.sleep(delay) await asyncio.sleep(delay)
t += delay t += delay
async def send_file(self, path, verbose): async def send_file(self, path: str, verbose=False) -> None:
# locally we do not need to do anything # locally we do not need to do anything
pass pass
async def mkdir(self, path, verbose=False): async def mkdir(self, path: str, verbose=False) -> None:
pathlib.Path(path).mkdir(parents=True, exist_ok=True) pathlib.Path(path).mkdir(parents=True, exist_ok=True)
async def rmtree(self, path, verbose=False): async def rmtree(self, path: str, verbose=False) -> None:
if os.path.isdir(path): if os.path.isdir(path):
shutil.rmtree(path, ignore_errors=True) shutil.rmtree(path, ignore_errors=True)
elif os.path.exists(path): elif os.path.exists(path):
...@@ -375,7 +390,7 @@ class LocalExecutor(Executor): ...@@ -375,7 +390,7 @@ class LocalExecutor(Executor):
class RemoteExecutor(Executor): class RemoteExecutor(Executor):
def __init__(self, host_name, workdir): def __init__(self, host_name: str, workdir: str) -> None:
super().__init__() super().__init__()
self.host_name = host_name self.host_name = host_name
...@@ -383,7 +398,9 @@ class RemoteExecutor(Executor): ...@@ -383,7 +398,9 @@ class RemoteExecutor(Executor):
self.ssh_extra_args = [] self.ssh_extra_args = []
self.scp_extra_args = [] self.scp_extra_args = []
def create_component(self, label, parts, **kwargs): def create_component(
self, label: str, parts: tp.List[str], **kwargs
) -> SimpleRemoteComponent:
return SimpleRemoteComponent( return SimpleRemoteComponent(
self.host_name, self.host_name,
label, label,
...@@ -393,7 +410,9 @@ class RemoteExecutor(Executor): ...@@ -393,7 +410,9 @@ class RemoteExecutor(Executor):
**kwargs **kwargs
) )
async def await_file(self, path, delay=0.05, verbose=False, timeout=30): async def await_file(
self, path: str, delay=0.05, verbose=False, timeout=30
) -> None:
if verbose: if verbose:
print(f'{self.host_name}.await_file({path}) started') print(f'{self.host_name}.await_file({path}) started')
...@@ -416,7 +435,7 @@ class RemoteExecutor(Executor): ...@@ -416,7 +435,7 @@ class RemoteExecutor(Executor):
# TODO: Implement opitimized await_files() # TODO: Implement opitimized await_files()
async def send_file(self, path, verbose): async def send_file(self, path: str, verbose=False) -> None:
parts = [ parts = [
'scp', 'scp',
'-o', '-o',
...@@ -433,7 +452,7 @@ class RemoteExecutor(Executor): ...@@ -433,7 +452,7 @@ class RemoteExecutor(Executor):
await sc.start() await sc.start()
await sc.wait() await sc.wait()
async def mkdir(self, path, verbose=False): async def mkdir(self, path: str, verbose=False) -> None:
sc = self.create_component( sc = self.create_component(
f"{self.host_name}.mkdir('{path}')", ['mkdir', '-p', path], f"{self.host_name}.mkdir('{path}')", ['mkdir', '-p', path],
canfail=False, canfail=False,
...@@ -442,7 +461,7 @@ class RemoteExecutor(Executor): ...@@ -442,7 +461,7 @@ class RemoteExecutor(Executor):
await sc.start() await sc.start()
await sc.wait() await sc.wait()
async def rmtree(self, path, verbose=False): async def rmtree(self, path: str, verbose=False) -> None:
sc = self.create_component( sc = self.create_component(
f'{self.host_name}.rmtree("{path}")', ['rm', '-rf', path], f'{self.host_name}.rmtree("{path}")', ['rm', '-rf', path],
canfail=False, canfail=False,
......
...@@ -21,12 +21,16 @@ ...@@ -21,12 +21,16 @@
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import os import os
import typing as tp
if tp.TYPE_CHECKING: # prevent cyclic import
from simbricks.orchestration import simulators
class ExpEnv(object): class ExpEnv(object):
"""Manages the experiment environment.""" """Manages the experiment environment."""
def __init__(self, repo_path, workdir, cpdir): def __init__(self, repo_path, workdir, cpdir) -> None:
self.create_cp = False self.create_cp = False
"""Whether a checkpoint should be created.""" """Whether a checkpoint should be created."""
self.restore_cp = False self.restore_cp = False
...@@ -53,50 +57,54 @@ class ExpEnv(object): ...@@ -53,50 +57,54 @@ class ExpEnv(object):
f'{simics_project_base}/targets/qsp-x86/qsp-modern-core.simics' f'{simics_project_base}/targets/qsp-x86/qsp-modern-core.simics'
) )
def gem5_path(self, variant): def gem5_path(self, variant: str) -> str:
return f'{self.repodir}/sims/external/gem5/build/X86/gem5.{variant}' return f'{self.repodir}/sims/external/gem5/build/X86/gem5.{variant}'
def hdcopy_path(self, sim): def hdcopy_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.workdir}/hdcopy.{sim.name}' return f'{self.workdir}/hdcopy.{sim.name}'
def hd_path(self, hd_name): def hd_path(self, hd_name: str) -> str:
return f'{self.repodir}/images/output-{hd_name}/{hd_name}' return f'{self.repodir}/images/output-{hd_name}/{hd_name}'
def hd_raw_path(self, hd_name): def hd_raw_path(self, hd_name: str) -> str:
return f'{self.repodir}/images/output-{hd_name}/{hd_name}.raw' return f'{self.repodir}/images/output-{hd_name}/{hd_name}.raw'
def cfgtar_path(self, sim): def cfgtar_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.workdir}/cfg.{sim.name}.tar' return f'{self.workdir}/cfg.{sim.name}.tar'
def dev_pci_path(self, sim): def dev_pci_path(self, sim) -> str:
return f'{self.workdir}/dev.pci.{sim.name}' return f'{self.workdir}/dev.pci.{sim.name}'
def dev_mem_path(self, sim): def dev_mem_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.workdir}/dev.mem.{sim.name}' return f'{self.workdir}/dev.mem.{sim.name}'
def nic_eth_path(self, sim): def nic_eth_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.workdir}/nic.eth.{sim.name}' return f'{self.workdir}/nic.eth.{sim.name}'
def dev_shm_path(self, sim): def dev_shm_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.shm_base}/dev.shm.{sim.name}' return f'{self.shm_base}/dev.shm.{sim.name}'
def n2n_eth_path(self, sim_l, sim_c): def n2n_eth_path(
self, sim_l: 'simulators.Simulator', sim_c: 'simulators.Simulator'
) -> str:
return f'{self.workdir}/n2n.eth.{sim_l.name}.{sim_c.name}' return f'{self.workdir}/n2n.eth.{sim_l.name}.{sim_c.name}'
def net2host_eth_path(self, sim_n, sim_h): def net2host_eth_path(self, sim_n, sim_h) -> str:
return f'{self.workdir}/n2h.eth.{sim_n.name}.{sim_h.name}' return f'{self.workdir}/n2h.eth.{sim_n.name}.{sim_h.name}'
def net2host_shm_path(self, sim_n, sim_h): def net2host_shm_path(
self, sim_n: 'simulators.Simulator', sim_h: 'simulators.Simulator'
) -> str:
return f'{self.workdir}/n2h.shm.{sim_n.name}.{sim_h.name}' return f'{self.workdir}/n2h.shm.{sim_n.name}.{sim_h.name}'
def proxy_shm_path(self, sim): 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 gem5_outdir(self, sim): def gem5_outdir(self, sim: 'simulators.Simulator') -> str:
return f'{self.workdir}/gem5-out.{sim.name}' return f'{self.workdir}/gem5-out.{sim.name}'
def gem5_cpdir(self, sim): def gem5_cpdir(self, sim: 'simulators.Simulator') -> str:
return f'{self.cpdir}/gem5-cp.{sim.name}' return f'{self.cpdir}/gem5-cp.{sim.name}'
def simics_cpfile(self, sim): def simics_cpfile(self, sim: 'simulators.Simulator') -> str:
return f'{self.cpdir}/simics-cp.{sim.name}' return f'{self.cpdir}/simics-cp.{sim.name}'
...@@ -23,36 +23,42 @@ ...@@ -23,36 +23,42 @@
import json import json
import pathlib import pathlib
import time import time
import typing as tp
from simbricks.orchestration.experiments import Experiment from simbricks.orchestration.experiments import Experiment
if tp.TYPE_CHECKING: # prevent cyclic import
from simbricks.orchestration import exectools, simulators
class ExpOutput(object): class ExpOutput(object):
"""Manages an experiment's output.""" """Manages an experiment's output."""
def __init__(self, exp: Experiment): def __init__(self, exp: Experiment) -> None:
self.exp_name = exp.name self.exp_name = exp.name
self.metadata = exp.metadata self.metadata = exp.metadata
self.start_time = None self.start_time = None
self.end_time = None self.end_time = None
self.sims = {} self.sims: tp.Dict[str, tp.Dict[str, tp.Union[str, tp.List[str]]]] = {}
self.success = True self.success = True
self.interrupted = False self.interrupted = False
def set_start(self): def set_start(self) -> None:
self.start_time = time.time() self.start_time = time.time()
def set_end(self): def set_end(self) -> None:
self.end_time = time.time() self.end_time = time.time()
def set_failed(self): def set_failed(self) -> None:
self.success = False self.success = False
def set_interrupted(self): def set_interrupted(self) -> None:
self.success = False self.success = False
self.interrupted = True self.interrupted = True
def add_sim(self, sim, comp): def add_sim(
self, sim: 'simulators.Simulator', comp: 'exectools.Component'
) -> None:
obj = { obj = {
'class': sim.__class__.__name__, 'class': sim.__class__.__name__,
'cmd': comp.cmd_parts, 'cmd': comp.cmd_parts,
...@@ -61,12 +67,12 @@ class ExpOutput(object): ...@@ -61,12 +67,12 @@ class ExpOutput(object):
} }
self.sims[sim.full_name()] = obj self.sims[sim.full_name()] = obj
def dump(self, outpath: str): def dump(self, outpath: str) -> None:
pathlib.Path(outpath).parent.mkdir(parents=True, exist_ok=True) pathlib.Path(outpath).parent.mkdir(parents=True, exist_ok=True)
with open(outpath, 'w', encoding='utf-8') as file: with open(outpath, 'w', encoding='utf-8') as file:
json.dump(self.__dict__, file) json.dump(self.__dict__, file)
def load(self, file: str): def load(self, file: str) -> None:
with open(file, 'r', encoding='utf-8') as fp: with open(file, 'r', encoding='utf-8') as fp:
for k, v in json.load(fp).items(): for k, v in json.load(fp).items():
self.__dict__[k] = v self.__dict__[k] = v
...@@ -37,7 +37,7 @@ class Experiment(object): ...@@ -37,7 +37,7 @@ class Experiment(object):
Contains the simulators to be run and experiment-wide parameters. Contains the simulators to be run and experiment-wide parameters.
""" """
def __init__(self, name: str): def __init__(self, name: str) -> None:
self.name = name self.name = name
""" """
This experiment's name. Can be used to run only a selection of This experiment's name. Can be used to run only a selection of
...@@ -64,13 +64,13 @@ class Experiment(object): ...@@ -64,13 +64,13 @@ class Experiment(object):
"""The network memory simulators to run.""" """The network memory simulators to run."""
self.networks: tp.List[NetSim] = [] self.networks: tp.List[NetSim] = []
"""The network simulators to run.""" """The network simulators to run."""
self.metadata = {} self.metadata: tp.Dict[str, tp.Any] = {}
@property @property
def nics(self): def nics(self):
return filter(lambda pcidev: pcidev.is_nic(), self.pcidevs) return filter(lambda pcidev: pcidev.is_nic(), self.pcidevs)
def add_host(self, sim: HostSim): def add_host(self, sim: HostSim) -> None:
"""Add a host simulator to the experiment.""" """Add a host simulator to the experiment."""
for h in self.hosts: for h in self.hosts:
if h.name == sim.name: if h.name == sim.name:
...@@ -81,7 +81,7 @@ class Experiment(object): ...@@ -81,7 +81,7 @@ class Experiment(object):
"""Add a NIC simulator to the experiment.""" """Add a NIC simulator to the experiment."""
self.add_pcidev(sim) self.add_pcidev(sim)
def add_pcidev(self, sim: PCIDevSim): def add_pcidev(self, sim: PCIDevSim) -> None:
"""Add a PCIe device simulator to the experiment.""" """Add a PCIe device simulator to the experiment."""
for d in self.pcidevs: for d in self.pcidevs:
if d.name == sim.name: if d.name == sim.name:
...@@ -100,27 +100,27 @@ class Experiment(object): ...@@ -100,27 +100,27 @@ class Experiment(object):
raise ValueError('Duplicate netmems name') raise ValueError('Duplicate netmems name')
self.netmems.append(sim) self.netmems.append(sim)
def add_network(self, sim: NetSim): def add_network(self, sim: NetSim) -> None:
"""Add a network simulator to the experiment.""" """Add a network simulator to the experiment."""
for n in self.networks: for n in self.networks:
if n.name == sim.name: if n.name == sim.name:
raise ValueError('Duplicate net name') raise ValueError('Duplicate net name')
self.networks.append(sim) self.networks.append(sim)
def all_simulators(self): def all_simulators(self) -> tp.Iterable[Simulator]:
"""Returns all simulators defined to run in this experiment.""" """Returns all simulators defined to run in this experiment."""
return itertools.chain( return itertools.chain(
self.hosts, self.pcidevs, self.memdevs, self.netmems, self.networks self.hosts, self.pcidevs, self.memdevs, self.netmems, self.networks
) )
def resreq_mem(self): def resreq_mem(self) -> int:
"""Memory required to run all simulators in this experiment.""" """Memory required to run all simulators in this experiment."""
mem = 0 mem = 0
for s in self.all_simulators(): for s in self.all_simulators():
mem += s.resreq_mem() mem += s.resreq_mem()
return mem return mem
def resreq_cores(self): def resreq_cores(self) -> int:
"""Number of Cores required to run all simulators in this experiment.""" """Number of Cores required to run all simulators in this experiment."""
cores = 0 cores = 0
for s in self.all_simulators(): for s in self.all_simulators():
...@@ -131,7 +131,7 @@ class Experiment(object): ...@@ -131,7 +131,7 @@ class Experiment(object):
class DistributedExperiment(Experiment): class DistributedExperiment(Experiment):
"""Describes a distributed simulation experiment.""" """Describes a distributed simulation experiment."""
def __init__(self, name: str, num_hosts: int): def __init__(self, name: str, num_hosts: int) -> None:
super().__init__(name) super().__init__(name)
self.num_hosts = num_hosts self.num_hosts = num_hosts
"""Number of hosts to use.""" """Number of hosts to use."""
...@@ -146,17 +146,17 @@ class DistributedExperiment(Experiment): ...@@ -146,17 +146,17 @@ class DistributedExperiment(Experiment):
else: else:
self.proxies_connect.append(tp.cast(NetProxyConnecter, proxy)) self.proxies_connect.append(tp.cast(NetProxyConnecter, proxy))
def all_simulators(self): def all_simulators(self) -> tp.Iterable[Simulator]:
return itertools.chain( return itertools.chain(
super().all_simulators(), self.proxies_listen, self.proxies_connect super().all_simulators(), self.proxies_listen, self.proxies_connect
) )
def assign_sim_host(self, sim: Simulator, host: int): def assign_sim_host(self, sim: Simulator, host: int) -> None:
"""Assign host ID (< self.num_hosts) for a simulator.""" """Assign host ID (< self.num_hosts) for a simulator."""
assert 0 <= host < self.num_hosts assert 0 <= host < self.num_hosts
self.host_mapping[sim] = host self.host_mapping[sim] = host
def all_sims_assigned(self): def all_sims_assigned(self) -> bool:
"""Check if all simulators are assigned to a host.""" """Check if all simulators are assigned to a host."""
for s in self.all_simulators(): for s in self.all_simulators():
if s not in self.host_mapping: if s not in self.host_mapping:
......
...@@ -54,13 +54,13 @@ class AppConfig(): ...@@ -54,13 +54,13 @@ class AppConfig():
""" """
return {} return {}
def strfile(self, s: str): def strfile(self, s: str) -> io.BytesIO:
""" """
Helper function to convert a string to an IO handle for usage in Helper function to convert a string to an IO handle for usage in
`config_files()`. `config_files()`.
Using this, you can create a file with the string as its content on the Using this, you can create a file with the string as its content on the
node. simulated node.
""" """
return io.BytesIO(bytes(s, encoding='UTF-8')) return io.BytesIO(bytes(s, encoding='UTF-8'))
...@@ -68,7 +68,7 @@ class AppConfig(): ...@@ -68,7 +68,7 @@ class AppConfig():
class NodeConfig(): class NodeConfig():
"""Defines the configuration of a node or host.""" """Defines the configuration of a node or host."""
def __init__(self): def __init__(self) -> None:
self.sim = 'qemu' self.sim = 'qemu'
"""The concrete simulator that runs this node config. This is used to """The concrete simulator that runs this node config. This is used to
use execute different commands depending on the concrete simulator, use execute different commands depending on the concrete simulator,
...@@ -115,7 +115,7 @@ class NodeConfig(): ...@@ -115,7 +115,7 @@ class NodeConfig():
self.run_cmds() + self.cleanup_cmds() + exit_es self.run_cmds() + self.cleanup_cmds() + exit_es
return '\n'.join(es) return '\n'.join(es)
def make_tar(self, path): def make_tar(self, path: str) -> None:
with tarfile.open(path, 'w:') as tar: with tarfile.open(path, 'w:') as tar:
# add main run script # add main run script
cfg_i = tarfile.TarInfo('guest/run.sh') cfg_i = tarfile.TarInfo('guest/run.sh')
...@@ -169,26 +169,26 @@ class NodeConfig(): ...@@ -169,26 +169,26 @@ class NodeConfig():
""" """
return self.app.config_files() return self.app.config_files()
def strfile(self, s: str): def strfile(self, s: str) -> io.BytesIO:
""" """
Helper function to convert a string to an IO handle for usage in Helper function to convert a string to an IO handle for usage in
`config_files()`. `config_files()`.
Using this, you can create a file with the string as its content on the Using this, you can create a file with the string as its content on the
node. simulated node.
""" """
return io.BytesIO(bytes(s, encoding='UTF-8')) return io.BytesIO(bytes(s, encoding='UTF-8'))
class LinuxNode(NodeConfig): class LinuxNode(NodeConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.ifname = 'eth0' self.ifname = 'eth0'
self.drivers = [] self.drivers: tp.List[str] = []
self.force_mac_addr = None self.force_mac_addr: tp.Optional[str] = None
def prepare_post_cp(self): def prepare_post_cp(self) -> tp.List[str]:
l = [] l = []
for d in self.drivers: for d in self.drivers:
if d[0] == '/': if d[0] == '/':
...@@ -207,40 +207,40 @@ class LinuxNode(NodeConfig): ...@@ -207,40 +207,40 @@ class LinuxNode(NodeConfig):
class I40eLinuxNode(LinuxNode): class I40eLinuxNode(LinuxNode):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.drivers.append('i40e') self.drivers.append('i40e')
class CorundumLinuxNode(LinuxNode): class CorundumLinuxNode(LinuxNode):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.drivers.append('/tmp/guest/mqnic.ko') self.drivers.append('/tmp/guest/mqnic.ko')
# pylint: disable=consider-using-with # pylint: disable=consider-using-with
def config_files(self): def config_files(self) -> tp.Dict[str, tp.IO]:
m = {'mqnic.ko': open('../images/mqnic/mqnic.ko', 'rb')} m = {'mqnic.ko': open('../images/mqnic/mqnic.ko', 'rb')}
return {**m, **super().config_files()} return {**m, **super().config_files()}
class E1000LinuxNode(LinuxNode): class E1000LinuxNode(LinuxNode):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.drivers.append('e1000') self.drivers.append('e1000')
class MtcpNode(NodeConfig): class MtcpNode(NodeConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.disk_image = 'mtcp' self.disk_image = 'mtcp'
self.pci_dev = '0000:00:02.0' self.pci_dev = '0000:00:02.0'
self.memory = 16 * 1024 self.memory = 16 * 1024
self.num_hugepages = 4096 self.num_hugepages = 4096
def prepare_pre_cp(self): def prepare_pre_cp(self) -> tp.List[str]:
return super().prepare_pre_cp() + [ return super().prepare_pre_cp() + [
'mount -t proc proc /proc', 'mount -t proc proc /proc',
'mount -t sysfs sysfs /sys', 'mount -t sysfs sysfs /sys',
...@@ -252,7 +252,7 @@ class MtcpNode(NodeConfig): ...@@ -252,7 +252,7 @@ class MtcpNode(NodeConfig):
'node/node0/hugepages/hugepages-2048kB/nr_hugepages', 'node/node0/hugepages/hugepages-2048kB/nr_hugepages',
] ]
def prepare_post_cp(self): def prepare_post_cp(self) -> tp.List[str]:
return super().prepare_post_cp() + [ return super().prepare_post_cp() + [
'insmod /root/mtcp/dpdk/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko', 'insmod /root/mtcp/dpdk/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko',
'/root/mtcp/dpdk/usertools/dpdk-devbind.py -b igb_uio ' + '/root/mtcp/dpdk/usertools/dpdk-devbind.py -b igb_uio ' +
...@@ -263,7 +263,7 @@ class MtcpNode(NodeConfig): ...@@ -263,7 +263,7 @@ class MtcpNode(NodeConfig):
f'ip addr add {self.ip}/{self.prefix} dev dpdk0' f'ip addr add {self.ip}/{self.prefix} dev dpdk0'
] ]
def config_files(self): def config_files(self) -> tp.Dict[str, tp.IO]:
m = { m = {
'mtcp.conf': 'mtcp.conf':
self.strfile( self.strfile(
...@@ -286,7 +286,7 @@ class MtcpNode(NodeConfig): ...@@ -286,7 +286,7 @@ class MtcpNode(NodeConfig):
class TASNode(NodeConfig): class TASNode(NodeConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.disk_image = 'tas' self.disk_image = 'tas'
self.pci_dev = '0000:00:02.0' self.pci_dev = '0000:00:02.0'
...@@ -295,7 +295,7 @@ class TASNode(NodeConfig): ...@@ -295,7 +295,7 @@ class TASNode(NodeConfig):
self.fp_cores = 1 self.fp_cores = 1
self.preload = True self.preload = True
def prepare_pre_cp(self): def prepare_pre_cp(self) -> tp.List[str]:
return super().prepare_pre_cp() + [ return super().prepare_pre_cp() + [
'mount -t proc proc /proc', 'mount -t proc proc /proc',
'mount -t sysfs sysfs /sys', 'mount -t sysfs sysfs /sys',
...@@ -307,7 +307,7 @@ class TASNode(NodeConfig): ...@@ -307,7 +307,7 @@ class TASNode(NodeConfig):
'node/node0/hugepages/hugepages-2048kB/nr_hugepages', 'node/node0/hugepages/hugepages-2048kB/nr_hugepages',
] ]
def prepare_post_cp(self): def prepare_post_cp(self) -> tp.List[str]:
cmds = super().prepare_post_cp() + [ cmds = super().prepare_post_cp() + [
'insmod /root/dpdk/lib/modules/5.4.46/extra/dpdk/igb_uio.ko', 'insmod /root/dpdk/lib/modules/5.4.46/extra/dpdk/igb_uio.ko',
'/root/dpdk/sbin/dpdk-devbind -b igb_uio ' + self.pci_dev, '/root/dpdk/sbin/dpdk-devbind -b igb_uio ' + self.pci_dev,
...@@ -326,7 +326,7 @@ class TASNode(NodeConfig): ...@@ -326,7 +326,7 @@ class TASNode(NodeConfig):
class I40eDCTCPNode(NodeConfig): class I40eDCTCPNode(NodeConfig):
def prepare_pre_cp(self): def prepare_pre_cp(self) -> tp.List[str]:
return super().prepare_pre_cp() + [ return super().prepare_pre_cp() + [
'mount -t proc proc /proc', 'mount -t proc proc /proc',
'mount -t sysfs sysfs /sys', 'mount -t sysfs sysfs /sys',
...@@ -342,7 +342,7 @@ class I40eDCTCPNode(NodeConfig): ...@@ -342,7 +342,7 @@ class I40eDCTCPNode(NodeConfig):
'sysctl -w net.ipv4.tcp_ecn=1' 'sysctl -w net.ipv4.tcp_ecn=1'
] ]
def prepare_post_cp(self): def prepare_post_cp(self) -> tp.List[str]:
return super().prepare_post_cp() + [ return super().prepare_post_cp() + [
'modprobe i40e', 'modprobe i40e',
'ethtool -G eth0 rx 4096 tx 4096', 'ethtool -G eth0 rx 4096 tx 4096',
...@@ -355,7 +355,7 @@ class I40eDCTCPNode(NodeConfig): ...@@ -355,7 +355,7 @@ class I40eDCTCPNode(NodeConfig):
class CorundumDCTCPNode(NodeConfig): class CorundumDCTCPNode(NodeConfig):
def prepare_pre_cp(self): def prepare_pre_cp(self) -> tp.List[str]:
return super().prepare_pre_cp() + [ return super().prepare_pre_cp() + [
'mount -t proc proc /proc', 'mount -t proc proc /proc',
'mount -t sysfs sysfs /sys', 'mount -t sysfs sysfs /sys',
...@@ -371,7 +371,7 @@ class CorundumDCTCPNode(NodeConfig): ...@@ -371,7 +371,7 @@ class CorundumDCTCPNode(NodeConfig):
'sysctl -w net.ipv4.tcp_ecn=1' 'sysctl -w net.ipv4.tcp_ecn=1'
] ]
def prepare_post_cp(self): def prepare_post_cp(self) -> tp.List[str]:
return super().prepare_post_cp() + [ return super().prepare_post_cp() + [
'insmod mqnic.ko', 'insmod mqnic.ko',
'ip link set dev eth0 up', 'ip link set dev eth0 up',
...@@ -381,11 +381,11 @@ class CorundumDCTCPNode(NodeConfig): ...@@ -381,11 +381,11 @@ class CorundumDCTCPNode(NodeConfig):
class LinuxFEMUNode(NodeConfig): class LinuxFEMUNode(NodeConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.drivers = ['nvme'] self.drivers = ['nvme']
def prepare_post_cp(self): def prepare_post_cp(self) -> tp.List[str]:
l = ['lspci -vvvv'] l = ['lspci -vvvv']
for d in self.drivers: for d in self.drivers:
if d[0] == '/': if d[0] == '/':
...@@ -397,13 +397,13 @@ class LinuxFEMUNode(NodeConfig): ...@@ -397,13 +397,13 @@ class LinuxFEMUNode(NodeConfig):
class IdleHost(AppConfig): class IdleHost(AppConfig):
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return ['sleep infinity'] return ['sleep infinity']
class NVMEFsTest(AppConfig): class NVMEFsTest(AppConfig):
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return [ return [
'mount -t proc proc /proc', 'mount -t proc proc /proc',
'mkfs.ext3 /dev/nvme0n1', 'mkfs.ext3 /dev/nvme0n1',
...@@ -414,18 +414,18 @@ class NVMEFsTest(AppConfig): ...@@ -414,18 +414,18 @@ class NVMEFsTest(AppConfig):
class DctcpServer(AppConfig): class DctcpServer(AppConfig):
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return ['iperf -s -w 1M -Z dctcp'] return ['iperf -s -w 1M -Z dctcp']
class DctcpClient(AppConfig): class DctcpClient(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ip = '192.168.64.1' self.server_ip = '192.168.64.1'
self.is_last = False self.is_last = False
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
if self.is_last: if self.is_last:
return [ return [
'sleep 1', 'sleep 1',
...@@ -442,35 +442,35 @@ class DctcpClient(AppConfig): ...@@ -442,35 +442,35 @@ class DctcpClient(AppConfig):
class PingClient(AppConfig): class PingClient(AppConfig):
def __init__(self, server_ip: str = '192.168.64.1'): def __init__(self, server_ip: str = '192.168.64.1') -> None:
super().__init__() super().__init__()
self.server_ip = server_ip self.server_ip = server_ip
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return [f'ping {self.server_ip} -c 10'] return [f'ping {self.server_ip} -c 10']
class IperfTCPServer(AppConfig): class IperfTCPServer(AppConfig):
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return ['iperf -s -l 32M -w 32M'] return ['iperf -s -l 32M -w 32M']
class IperfUDPServer(AppConfig): class IperfUDPServer(AppConfig):
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return ['iperf -s -u'] return ['iperf -s -u']
class IperfTCPClient(AppConfig): class IperfTCPClient(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ip = '10.0.0.1' self.server_ip = '10.0.0.1'
self.procs = 1 self.procs = 1
self.is_last = False self.is_last = False
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
cmds = [ cmds = [
'sleep 1', 'sleep 1',
...@@ -486,13 +486,13 @@ class IperfTCPClient(AppConfig): ...@@ -486,13 +486,13 @@ class IperfTCPClient(AppConfig):
class IperfUDPClient(AppConfig): class IperfUDPClient(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ip = '10.0.0.1' self.server_ip = '10.0.0.1'
self.rate = '150m' self.rate = '150m'
self.is_last = False self.is_last = False
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
cmds = [ cmds = [
'sleep 1', 'sleep 1',
'iperf -c ' + self.server_ip + ' -i 1 -u -b ' + self.rate 'iperf -c ' + self.server_ip + ' -i 1 -u -b ' + self.rate
...@@ -508,13 +508,13 @@ class IperfUDPClient(AppConfig): ...@@ -508,13 +508,13 @@ class IperfUDPClient(AppConfig):
class IperfUDPShortClient(AppConfig): class IperfUDPShortClient(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ip = '10.0.0.1' self.server_ip = '10.0.0.1'
self.rate = '150m' self.rate = '150m'
self.is_last = False self.is_last = False
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
cmds = ['sleep 1', 'iperf -c ' + self.server_ip + ' -u -n 1 '] cmds = ['sleep 1', 'iperf -c ' + self.server_ip + ' -u -n 1 ']
return cmds return cmds
...@@ -522,23 +522,23 @@ class IperfUDPShortClient(AppConfig): ...@@ -522,23 +522,23 @@ class IperfUDPShortClient(AppConfig):
class IperfUDPClientSleep(AppConfig): class IperfUDPClientSleep(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ip = '10.0.0.1' self.server_ip = '10.0.0.1'
self.rate = '150m' self.rate = '150m'
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return ['sleep 1', 'sleep 10'] return ['sleep 1', 'sleep 10']
class NoTraffic(AppConfig): class NoTraffic(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.is_sleep = 1 self.is_sleep = 1
self.is_server = 0 self.is_server = 0
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
cmds = [] cmds = []
if self.is_server: if self.is_server:
cmds.append('sleep infinity') cmds.append('sleep infinity')
...@@ -554,19 +554,19 @@ class NoTraffic(AppConfig): ...@@ -554,19 +554,19 @@ class NoTraffic(AppConfig):
class NetperfServer(AppConfig): class NetperfServer(AppConfig):
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return ['netserver', 'sleep infinity'] return ['netserver', 'sleep infinity']
class NetperfClient(AppConfig): class NetperfClient(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ip = '10.0.0.1' self.server_ip = '10.0.0.1'
self.duration_tp = 10 self.duration_tp = 10
self.duration_lat = 10 self.duration_lat = 10
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return [ return [
'netserver', 'netserver',
'sleep 0.5', 'sleep 0.5',
...@@ -580,11 +580,11 @@ class NetperfClient(AppConfig): ...@@ -580,11 +580,11 @@ class NetperfClient(AppConfig):
class VRReplica(AppConfig): class VRReplica(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.index = 0 self.index = 0
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return [ return [
'/root/nopaxos/bench/replica -c /root/nopaxos.config -i ' + '/root/nopaxos/bench/replica -c /root/nopaxos.config -i ' +
str(self.index) + ' -m vr' str(self.index) + ' -m vr'
...@@ -593,11 +593,11 @@ class VRReplica(AppConfig): ...@@ -593,11 +593,11 @@ class VRReplica(AppConfig):
class VRClient(AppConfig): class VRClient(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ips = [] self.server_ips: tp.List[str] = []
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
cmds = [] cmds = []
for ip in self.server_ips: for ip in self.server_ips:
cmds.append('ping -c 2 ' + ip) cmds.append('ping -c 2 ' + ip)
...@@ -610,11 +610,11 @@ class VRClient(AppConfig): ...@@ -610,11 +610,11 @@ class VRClient(AppConfig):
class NOPaxosReplica(AppConfig): class NOPaxosReplica(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.index = 0 self.index = 0
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return [ return [
'/root/nopaxos/bench/replica -c /root/nopaxos.config -i ' + '/root/nopaxos/bench/replica -c /root/nopaxos.config -i ' +
str(self.index) + ' -m nopaxos' str(self.index) + ' -m nopaxos'
...@@ -623,13 +623,13 @@ class NOPaxosReplica(AppConfig): ...@@ -623,13 +623,13 @@ class NOPaxosReplica(AppConfig):
class NOPaxosClient(AppConfig): class NOPaxosClient(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ips = [] self.server_ips: tp.List[str] = []
self.is_last = False self.is_last = False
self.use_ehseq = False self.use_ehseq = False
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
cmds = [] cmds = []
for ip in self.server_ips: for ip in self.server_ips:
cmds.append('ping -c 2 ' + ip) cmds.append('ping -c 2 ' + ip)
...@@ -647,7 +647,7 @@ class NOPaxosClient(AppConfig): ...@@ -647,7 +647,7 @@ class NOPaxosClient(AppConfig):
class NOPaxosSequencer(AppConfig): class NOPaxosSequencer(AppConfig):
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return [( return [(
'/root/nopaxos/sequencer/sequencer -c /root/nopaxos.config' '/root/nopaxos/sequencer/sequencer -c /root/nopaxos.config'
' -m nopaxos' ' -m nopaxos'
...@@ -656,14 +656,14 @@ class NOPaxosSequencer(AppConfig): ...@@ -656,14 +656,14 @@ class NOPaxosSequencer(AppConfig):
class RPCServer(AppConfig): class RPCServer(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.port = 1234 self.port = 1234
self.threads = 1 self.threads = 1
self.max_flows = 1234 self.max_flows = 1234
self.max_bytes = 1024 self.max_bytes = 1024
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
exe = 'echoserver_linux' if not isinstance(node, MtcpNode) else \ exe = 'echoserver_linux' if not isinstance(node, MtcpNode) else \
'echoserver_mtcp' 'echoserver_mtcp'
return [ return [
...@@ -677,7 +677,7 @@ class RPCServer(AppConfig): ...@@ -677,7 +677,7 @@ class RPCServer(AppConfig):
class RPCClient(AppConfig): class RPCClient(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ip = '10.0.0.1' self.server_ip = '10.0.0.1'
self.port = 1234 self.port = 1234
...@@ -690,7 +690,7 @@ class RPCClient(AppConfig): ...@@ -690,7 +690,7 @@ class RPCClient(AppConfig):
self.max_pend_conns = 8 self.max_pend_conns = 8
self.time = 25 self.time = 25
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
exe = 'testclient_linux' if not isinstance(node, MtcpNode) else \ exe = 'testclient_linux' if not isinstance(node, MtcpNode) else \
'testclient_mtcp' 'testclient_mtcp'
return [ return [
...@@ -710,14 +710,14 @@ class RPCClient(AppConfig): ...@@ -710,14 +710,14 @@ class RPCClient(AppConfig):
class HTTPD(AppConfig): class HTTPD(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.threads = 1 self.threads = 1
self.file_size = 64 self.file_size = 64
self.mtcp_config = 'lighttpd.conf' self.mtcp_config = 'lighttpd.conf'
self.httpd_dir = '' # TODO added because doesn't originally exist self.httpd_dir = '' # TODO added because doesn't originally exist
def prepare_pre_cp(self): def prepare_pre_cp(self) -> tp.List[str]:
return [ return [
'mkdir -p /srv/www/htdocs/ /tmp/lighttpd/', 'mkdir -p /srv/www/htdocs/ /tmp/lighttpd/',
( (
...@@ -726,7 +726,7 @@ class HTTPD(AppConfig): ...@@ -726,7 +726,7 @@ class HTTPD(AppConfig):
) )
] ]
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return [ return [
f'cd {self.httpd_dir}/src/', f'cd {self.httpd_dir}/src/',
( (
...@@ -738,26 +738,26 @@ class HTTPD(AppConfig): ...@@ -738,26 +738,26 @@ class HTTPD(AppConfig):
class HTTPDLinux(HTTPD): class HTTPDLinux(HTTPD):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.httpd_dir = '/root/mtcp/apps/lighttpd-mtlinux' self.httpd_dir = '/root/mtcp/apps/lighttpd-mtlinux'
class HTTPDLinuxRPO(HTTPD): class HTTPDLinuxRPO(HTTPD):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.httpd_dir = '/root/mtcp/apps/lighttpd-mtlinux-rop' self.httpd_dir = '/root/mtcp/apps/lighttpd-mtlinux-rop'
class HTTPDMtcp(HTTPD): class HTTPDMtcp(HTTPD):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.httpd_dir = '/root/mtcp/apps/lighttpd-mtcp' self.httpd_dir = '/root/mtcp/apps/lighttpd-mtcp'
self.mtcp_config = 'm-lighttpd.conf' self.mtcp_config = 'm-lighttpd.conf'
def prepare_pre_cp(self): def prepare_pre_cp(self) -> tp.List[str]:
return super().prepare_pre_cp() + [ return super().prepare_pre_cp() + [
f'cp /tmp/guest/mtcp.conf {self.httpd_dir}/src/mtcp.conf', f'cp /tmp/guest/mtcp.conf {self.httpd_dir}/src/mtcp.conf',
( (
...@@ -770,7 +770,7 @@ class HTTPDMtcp(HTTPD): ...@@ -770,7 +770,7 @@ class HTTPDMtcp(HTTPD):
class HTTPC(AppConfig): class HTTPC(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ip = '10.0.0.1' self.server_ip = '10.0.0.1'
self.conns = 1000 self.conns = 1000
...@@ -780,7 +780,7 @@ class HTTPC(AppConfig): ...@@ -780,7 +780,7 @@ class HTTPC(AppConfig):
self.url = '/file' self.url = '/file'
self.ab_dir = '' # TODO added because doesn't originally exist self.ab_dir = '' # TODO added because doesn't originally exist
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return [ return [
f'cd {self.ab_dir}/support/', f'cd {self.ab_dir}/support/',
( (
...@@ -792,18 +792,18 @@ class HTTPC(AppConfig): ...@@ -792,18 +792,18 @@ class HTTPC(AppConfig):
class HTTPCLinux(HTTPC): class HTTPCLinux(HTTPC):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.ab_dir = '/root/mtcp/apps/ab-linux' self.ab_dir = '/root/mtcp/apps/ab-linux'
class HTTPCMtcp(HTTPC): class HTTPCMtcp(HTTPC):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.ab_dir = '/root/mtcp/apps/ab-mtcp' self.ab_dir = '/root/mtcp/apps/ab-mtcp'
def prepare_pre_cp(self): def prepare_pre_cp(self) -> tp.List[str]:
return super().prepare_pre_cp() + [ return super().prepare_pre_cp() + [
f'cp /tmp/guest/mtcp.conf {self.ab_dir}/support/config/mtcp.conf', f'cp /tmp/guest/mtcp.conf {self.ab_dir}/support/config/mtcp.conf',
f'rm -f {self.ab_dir}/support/config/arp.conf' f'rm -f {self.ab_dir}/support/config/arp.conf'
...@@ -812,20 +812,20 @@ class HTTPCMtcp(HTTPC): ...@@ -812,20 +812,20 @@ class HTTPCMtcp(HTTPC):
class MemcachedServer(AppConfig): class MemcachedServer(AppConfig):
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
return ['memcached -u root -t 1 -c 4096'] return ['memcached -u root -t 1 -c 4096']
class MemcachedClient(AppConfig): class MemcachedClient(AppConfig):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.server_ips = ['10.0.0.1'] self.server_ips = ['10.0.0.1']
self.threads = 1 self.threads = 1
self.concurrency = 1 self.concurrency = 1
self.throughput = '1k' self.throughput = '1k'
def run_cmds(self, node): def run_cmds(self, node: NodeConfig) -> tp.List[str]:
servers = [ip + ':11211' for ip in self.server_ips] servers = [ip + ':11211' for ip in self.server_ips]
servers = ','.join(servers) servers = ','.join(servers)
return [( return [(
......
...@@ -24,60 +24,61 @@ import typing as tp ...@@ -24,60 +24,61 @@ import typing as tp
from simbricks.orchestration.simulators import NICSim, Simulator from simbricks.orchestration.simulators import NICSim, Simulator
if tp.TYPE_CHECKING:
from simbricks.orchestration.experiment import experiment_environment
class SimProxy(Simulator): class SimProxy(Simulator):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.name = '' self.name = ''
# set by the experiment runner # set by the experiment runner
self.ip = '' self.ip = ''
self.listen = False self.listen = False
def full_name(self): def full_name(self) -> str:
return 'proxy.' + self.name return 'proxy.' + self.name
class NetProxy(SimProxy): class NetProxy(SimProxy):
"""Proxy for connections between NICs and networks.""" """Proxy for connections between NICs and networks."""
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.nics: tp.List[tp.Tuple[NICSim, bool]] = [] self.nics: tp.List[tp.Tuple[NICSim, bool]] = []
"""List of tuples (nic, with_listener)""" """List of tuples (nic, with_listener)"""
self.n2ns: tp.List[tp.Tuple[tp.Tuple[NetProxyListener, self.n2ns: tp.List[tp.Tuple[tp.Tuple[Simulator, Simulator], bool]] = []
NetProxyConnecter],
bool]] = []
"""List of tuples ((netL,netC), with_listener)""" """List of tuples ((netL,netC), with_listener)"""
self.shm_size = 2048 self.shm_size = 2048
"""Shared memory size in GB""" """Shared memory size in GB"""
def start_delay(self): def start_delay(self) -> int:
return 10 return 10
class NetProxyListener(NetProxy): class NetProxyListener(NetProxy):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.port = 12345 self.port = 12345
self.connecter = None self.connecter: NetProxyConnecter
self.listen = True self.listen = True
def add_nic(self, nic): def add_nic(self, nic: NICSim) -> None:
self.nics.append((nic, True)) self.nics.append((nic, True))
# the network this nic connects to now also depends on the peer # the network this nic connects to now also depends on the peer
nic.network.extra_deps.append(self.connecter) nic.network.extra_deps.append(self.connecter)
# add net2net connection with listening network on the listener side # add net2net connection with listening network on the listener side
def add_n2n(self, net_c, net_l): def add_n2n(self, net_c: Simulator, net_l: Simulator) -> None:
self.n2ns.append(((net_c, net_l), True)) self.n2ns.append(((net_c, net_l), True))
# the connecting network depends on our peer # the connecting network depends on our peer
net_c.extra_deps.append(self.connecter) net_c.extra_deps.append(self.connecter)
def dependencies(self): def dependencies(self) -> tp.List[Simulator]:
deps = [] deps = []
for (nic, local) in self.nics: for (nic, local) in self.nics:
if local: if local:
...@@ -87,7 +88,8 @@ class NetProxyListener(NetProxy): ...@@ -87,7 +88,8 @@ class NetProxyListener(NetProxy):
deps.append(net_l) deps.append(net_l)
return deps return deps
def sockets_cleanup(self, env): def sockets_cleanup(self,
env: 'experiment_environment.ExpEnv') -> tp.List[str]:
socks = [] socks = []
for (nic, local) in self.nics: for (nic, local) in self.nics:
if not local: if not local:
...@@ -98,7 +100,8 @@ class NetProxyListener(NetProxy): ...@@ -98,7 +100,8 @@ class NetProxyListener(NetProxy):
return [] return []
# sockets to wait for indicating the simulator is ready # sockets to wait for indicating the simulator is ready
def sockets_wait(self, env): def sockets_wait(self,
env: 'experiment_environment.ExpEnv') -> tp.List[str]:
socks = [] socks = []
for (nic, local) in self.nics: for (nic, local) in self.nics:
if not local: if not local:
...@@ -108,7 +111,7 @@ class NetProxyListener(NetProxy): ...@@ -108,7 +111,7 @@ class NetProxyListener(NetProxy):
socks.append(env.n2n_eth_path(net_l, net_c)) socks.append(env.n2n_eth_path(net_l, net_c))
return socks return socks
def run_cmd_base(self, env): def run_cmd_base(self, env: 'experiment_environment.ExpEnv') -> str:
cmd = (f'-s {env.proxy_shm_path(self)} ' cmd = (f'-s {env.proxy_shm_path(self)} '
f'-S {self.shm_size} ') f'-S {self.shm_size} ')
for (nic, local) in self.nics: for (nic, local) in self.nics:
...@@ -125,26 +128,26 @@ class NetProxyListener(NetProxy): ...@@ -125,26 +128,26 @@ class NetProxyListener(NetProxy):
class NetProxyConnecter(NetProxy): class NetProxyConnecter(NetProxy):
def __init__(self, listener: NetProxyListener): def __init__(self, listener: NetProxyListener) -> None:
super().__init__() super().__init__()
self.listener = listener self.listener = listener
listener.connecter = self listener.connecter = self
self.nics = listener.nics self.nics = listener.nics
self.n2ns = listener.n2ns self.n2ns = listener.n2ns
def add_nic(self, nic): def add_nic(self, nic: NICSim) -> None:
self.nics.append((nic, False)) self.nics.append((nic, False))
# the network this nic connects to now also depends on the proxy # the network this nic connects to now also depends on the proxy
nic.network.extra_deps.append(self.listener) nic.network.extra_deps.append(self.listener)
# add net2net connection with listening network on the connection side # add net2net connection with listening network on the connection side
def add_n2n(self, net_c, net_l): def add_n2n(self, net_c: Simulator, net_l: Simulator) -> None:
self.n2ns.append(((net_c, net_l), False)) self.n2ns.append(((net_c, net_l), False))
# the connecting network depends on our peer # the connecting network depends on our peer
net_c.extra_deps.append(self.listener) net_c.extra_deps.append(self.listener)
def dependencies(self): def dependencies(self) -> tp.List[Simulator]:
deps = [self.listener] deps = [self.listener]
for (nic, local) in self.nics: for (nic, local) in self.nics:
if not local: if not local:
...@@ -154,7 +157,8 @@ class NetProxyConnecter(NetProxy): ...@@ -154,7 +157,8 @@ class NetProxyConnecter(NetProxy):
deps.append(net_l) deps.append(net_l)
return deps return deps
def sockets_cleanup(self, env): def sockets_cleanup(self,
env: 'experiment_environment.ExpEnv') -> tp.List[str]:
socks = [] socks = []
for (nic, local) in self.nics: for (nic, local) in self.nics:
if local: if local:
...@@ -165,7 +169,8 @@ class NetProxyConnecter(NetProxy): ...@@ -165,7 +169,8 @@ class NetProxyConnecter(NetProxy):
return [] return []
# sockets to wait for indicating the simulator is ready # sockets to wait for indicating the simulator is ready
def sockets_wait(self, env): def sockets_wait(self,
env: 'experiment_environment.ExpEnv') -> tp.List[str]:
socks = [] socks = []
for (nic, local) in self.nics: for (nic, local) in self.nics:
if local: if local:
...@@ -175,7 +180,7 @@ class NetProxyConnecter(NetProxy): ...@@ -175,7 +180,7 @@ class NetProxyConnecter(NetProxy):
socks.append(env.n2n_eth_path(net_l, net_c)) socks.append(env.n2n_eth_path(net_l, net_c))
return socks return socks
def run_cmd_base(self, env): def run_cmd_base(self, env: 'experiment_environment.ExpEnv') -> str:
cmd = (f'-s {env.proxy_shm_path(self)} ' cmd = (f'-s {env.proxy_shm_path(self)} '
f'-S {self.shm_size} ') f'-S {self.shm_size} ')
for (nic, local) in self.nics: for (nic, local) in self.nics:
...@@ -192,7 +197,7 @@ class NetProxyConnecter(NetProxy): ...@@ -192,7 +197,7 @@ class NetProxyConnecter(NetProxy):
class RDMANetProxyListener(NetProxyListener): class RDMANetProxyListener(NetProxyListener):
def run_cmd(self, env): def run_cmd(self, env: 'experiment_environment.ExpEnv') -> str:
cmd = f'{env.repodir}/dist/rdma/net_rdma -l ' cmd = f'{env.repodir}/dist/rdma/net_rdma -l '
cmd += super().run_cmd_base(env) cmd += super().run_cmd_base(env)
return cmd return cmd
...@@ -200,7 +205,7 @@ class RDMANetProxyListener(NetProxyListener): ...@@ -200,7 +205,7 @@ class RDMANetProxyListener(NetProxyListener):
class RDMANetProxyConnecter(NetProxyConnecter): class RDMANetProxyConnecter(NetProxyConnecter):
def run_cmd(self, env): def run_cmd(self, env: 'experiment_environment.ExpEnv') -> str:
cmd = f'{env.repodir}/dist/rdma/net_rdma ' cmd = f'{env.repodir}/dist/rdma/net_rdma '
cmd += super().run_cmd_base(env) cmd += super().run_cmd_base(env)
return cmd return cmd
...@@ -208,7 +213,7 @@ class RDMANetProxyConnecter(NetProxyConnecter): ...@@ -208,7 +213,7 @@ class RDMANetProxyConnecter(NetProxyConnecter):
class SocketsNetProxyListener(NetProxyListener): class SocketsNetProxyListener(NetProxyListener):
def run_cmd(self, env): def run_cmd(self, env: 'experiment_environment.ExpEnv') -> str:
cmd = f'{env.repodir}/dist/sockets/net_sockets -l ' cmd = f'{env.repodir}/dist/sockets/net_sockets -l '
cmd += super().run_cmd_base(env) cmd += super().run_cmd_base(env)
return cmd return cmd
...@@ -216,7 +221,7 @@ class SocketsNetProxyListener(NetProxyListener): ...@@ -216,7 +221,7 @@ class SocketsNetProxyListener(NetProxyListener):
class SocketsNetProxyConnecter(NetProxyConnecter): class SocketsNetProxyConnecter(NetProxyConnecter):
def run_cmd(self, env): def run_cmd(self, env: 'experiment_environment.ExpEnv') -> str:
cmd = f'{env.repodir}/dist/sockets/net_sockets ' cmd = f'{env.repodir}/dist/sockets/net_sockets '
cmd += super().run_cmd_base(env) cmd += super().run_cmd_base(env)
return cmd return cmd
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import asyncio import asyncio
import collections
import itertools import itertools
import shlex import shlex
import traceback import traceback
...@@ -41,30 +42,29 @@ from simbricks.orchestration.utils import graphlib ...@@ -41,30 +42,29 @@ from simbricks.orchestration.utils import graphlib
class ExperimentBaseRunner(ABC): class ExperimentBaseRunner(ABC):
def __init__(self, exp: Experiment, env: ExpEnv, verbose: bool): def __init__(self, exp: Experiment, env: ExpEnv, verbose: bool) -> None:
self.exp = exp self.exp = exp
self.env = env self.env = env
self.verbose = verbose self.verbose = verbose
self.out = ExpOutput(exp) self.out = ExpOutput(exp)
self.running: tp.List[tp.Tuple[Simulator, SimpleComponent]] = [] self.running: tp.List[tp.Tuple[Simulator, SimpleComponent]] = []
self.sockets = [] self.sockets: tp.List[tp.Tuple[Executor, str]] = []
self.wait_sims: tp.List[Component] = [] self.wait_sims: tp.List[Component] = []
@abstractmethod @abstractmethod
def sim_executor(self, sim: Simulator) -> Executor: def sim_executor(self, sim: Simulator) -> Executor:
pass pass
def sim_graph(self): def sim_graph(self) -> tp.Dict[Simulator, tp.Set[Simulator]]:
sims = self.exp.all_simulators() sims = self.exp.all_simulators()
graph = {} graph = collections.defaultdict(set)
for sim in sims: for sim in sims:
deps = sim.dependencies() + sim.extra_deps deps = sim.dependencies() + sim.extra_deps
graph[sim] = set()
for d in deps: for d in deps:
graph[sim].add(d) graph[sim].add(d)
return graph return graph
async def start_sim(self, sim: Simulator): async def start_sim(self, sim: Simulator) -> None:
"""Start a simulator and wait for it to be ready.""" """Start a simulator and wait for it to be ready."""
name = sim.full_name() name = sim.full_name()
...@@ -108,16 +108,16 @@ class ExperimentBaseRunner(ABC): ...@@ -108,16 +108,16 @@ class ExperimentBaseRunner(ABC):
if self.verbose: if self.verbose:
print(f'{self.exp.name}: started {name}') print(f'{self.exp.name}: started {name}')
async def before_wait(self): async def before_wait(self) -> None:
pass pass
async def before_cleanup(self): async def before_cleanup(self) -> None:
pass pass
async def after_cleanup(self): async def after_cleanup(self) -> None:
pass pass
async def prepare(self): async def prepare(self) -> None:
# generate config tars # generate config tars
copies = [] copies = []
for host in self.exp.hosts: for host in self.exp.hosts:
...@@ -143,14 +143,14 @@ class ExperimentBaseRunner(ABC): ...@@ -143,14 +143,14 @@ class ExperimentBaseRunner(ABC):
sims.append(task) sims.append(task)
await asyncio.wait(sims) await asyncio.wait(sims)
async def wait_for_sims(self): async def wait_for_sims(self) -> None:
"""Wait for simulators to terminate (the ones marked to wait on).""" """Wait for simulators to terminate (the ones marked to wait on)."""
if self.verbose: if self.verbose:
print(f'{self.exp.name}: waiting for hosts to terminate') print(f'{self.exp.name}: waiting for hosts to terminate')
for sc in self.wait_sims: for sc in self.wait_sims:
await sc.wait() await sc.wait()
async def run(self): async def run(self) -> ExpOutput:
try: try:
self.out.set_start() self.out.set_start()
...@@ -218,28 +218,30 @@ class ExperimentBaseRunner(ABC): ...@@ -218,28 +218,30 @@ class ExperimentBaseRunner(ABC):
class ExperimentSimpleRunner(ExperimentBaseRunner): class ExperimentSimpleRunner(ExperimentBaseRunner):
"""Simple experiment runner with just one executor.""" """Simple experiment runner with just one executor."""
def __init__(self, executor: Executor, *args, **kwargs): def __init__(self, executor: Executor, *args, **kwargs) -> None:
self.executor = executor self.executor = executor
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def sim_executor(self, sim: Simulator): def sim_executor(self, sim: Simulator) -> Executor:
return self.executor return self.executor
class ExperimentDistributedRunner(ExperimentBaseRunner): class ExperimentDistributedRunner(ExperimentBaseRunner):
"""Simple experiment runner with just one executor.""" """Simple experiment runner with just one executor."""
def __init__(self, execs, exp: DistributedExperiment, *args, **kwargs): def __init__(
self, execs, exp: DistributedExperiment, *args, **kwargs
) -> None:
self.execs = execs self.execs = execs
super().__init__(exp, *args, **kwargs) super().__init__(exp, *args, **kwargs)
self.exp = exp # overrides the type in the base class self.exp = exp # overrides the type in the base class
assert self.exp.num_hosts <= len(execs) assert self.exp.num_hosts <= len(execs)
def sim_executor(self, sim): def sim_executor(self, sim) -> Executor:
h_id = self.exp.host_mapping[sim] h_id = self.exp.host_mapping[sim]
return self.execs[h_id] return self.execs[h_id]
async def prepare(self): async def prepare(self) -> None:
# make sure all simulators are assigned to an executor # make sure all simulators are assigned to an executor
assert self.exp.all_sims_assigned() assert self.exp.all_sims_assigned()
......
...@@ -54,10 +54,10 @@ class Run(object): ...@@ -54,10 +54,10 @@ class Run(object):
self.job_id: tp.Optional[int] = None self.job_id: tp.Optional[int] = None
"""Slurm job id.""" """Slurm job id."""
def name(self): def name(self) -> str:
return self.experiment.name + '.' + str(self.index) return self.experiment.name + '.' + str(self.index)
async def prep_dirs(self, executor=LocalExecutor()): async def prep_dirs(self, executor=LocalExecutor()) -> None:
shutil.rmtree(self.env.workdir, ignore_errors=True) shutil.rmtree(self.env.workdir, ignore_errors=True)
await executor.rmtree(self.env.workdir) await executor.rmtree(self.env.workdir)
shutil.rmtree(self.env.shm_base, ignore_errors=True) shutil.rmtree(self.env.shm_base, ignore_errors=True)
...@@ -83,11 +83,11 @@ class Runtime(metaclass=ABCMeta): ...@@ -83,11 +83,11 @@ class Runtime(metaclass=ABCMeta):
"""Indicates whether interrupt has been signaled.""" """Indicates whether interrupt has been signaled."""
@abstractmethod @abstractmethod
def add_run(self, run: Run): def add_run(self, run: Run) -> None:
pass pass
@abstractmethod @abstractmethod
async def start(self): async def start(self) -> None:
pass pass
@abstractmethod @abstractmethod
......
...@@ -34,7 +34,7 @@ from simbricks.orchestration.runtime.common import Run, Runtime ...@@ -34,7 +34,7 @@ from simbricks.orchestration.runtime.common import Run, Runtime
class DistributedSimpleRuntime(Runtime): class DistributedSimpleRuntime(Runtime):
def __init__(self, executors, verbose=False): def __init__(self, executors, verbose=False) -> None:
super().__init__() super().__init__()
self.runnable: tp.List[Run] = [] self.runnable: tp.List[Run] = []
self.complete: tp.List[Run] = [] self.complete: tp.List[Run] = []
...@@ -43,13 +43,13 @@ class DistributedSimpleRuntime(Runtime): ...@@ -43,13 +43,13 @@ class DistributedSimpleRuntime(Runtime):
self._running: asyncio.Task self._running: asyncio.Task
def add_run(self, run: Run): def add_run(self, run: Run) -> None:
if not isinstance(run.experiment, DistributedExperiment): if not isinstance(run.experiment, DistributedExperiment):
raise RuntimeError('Only distributed experiments supported') raise RuntimeError('Only distributed experiments supported')
self.runnable.append(run) self.runnable.append(run)
async def do_run(self, run: Run): async def do_run(self, run: Run) -> None:
runner = ExperimentDistributedRunner( runner = ExperimentDistributedRunner(
self.executors, self.executors,
# we ensure the correct type in add_run() # we ensure the correct type in add_run()
...@@ -91,8 +91,10 @@ class DistributedSimpleRuntime(Runtime): ...@@ -91,8 +91,10 @@ class DistributedSimpleRuntime(Runtime):
def auto_dist( def auto_dist(
e: Experiment, execs: tp.List[Executor], proxy_type: str = 'sockets' e: Experiment,
): execs: tp.List[Executor],
proxy_type: str = 'sockets'
) -> DistributedExperiment:
""" """
Converts an Experiment into a DistributedExperiment. Converts an Experiment into a DistributedExperiment.
......
...@@ -43,10 +43,10 @@ class LocalSimpleRuntime(Runtime): ...@@ -43,10 +43,10 @@ class LocalSimpleRuntime(Runtime):
self.executor = executor self.executor = executor
self._running: tp.Optional[asyncio.Task] = None self._running: tp.Optional[asyncio.Task] = None
def add_run(self, run: Run): def add_run(self, run: Run) -> None:
self.runnable.append(run) self.runnable.append(run)
async def do_run(self, run: Run): async def do_run(self, run: Run) -> None:
"""Actually executes `run`.""" """Actually executes `run`."""
try: try:
runner = ExperimentSimpleRunner( runner = ExperimentSimpleRunner(
...@@ -69,7 +69,7 @@ class LocalSimpleRuntime(Runtime): ...@@ -69,7 +69,7 @@ class LocalSimpleRuntime(Runtime):
) )
run.output.dump(run.outpath) run.output.dump(run.outpath)
async def start(self): async def start(self) -> None:
"""Execute the runs defined in `self.runnable`.""" """Execute the runs defined in `self.runnable`."""
for run in self.runnable: for run in self.runnable:
if self._interrupted: if self._interrupted:
...@@ -78,7 +78,7 @@ class LocalSimpleRuntime(Runtime): ...@@ -78,7 +78,7 @@ class LocalSimpleRuntime(Runtime):
self._running = asyncio.create_task(self.do_run(run)) self._running = asyncio.create_task(self.do_run(run))
await self._running await self._running
def interrupt_handler(self): def interrupt_handler(self) -> None:
if self._running: if self._running:
self._running.cancel() self._running.cancel()
...@@ -107,7 +107,7 @@ class LocalParallelRuntime(Runtime): ...@@ -107,7 +107,7 @@ class LocalParallelRuntime(Runtime):
self._pending_jobs: tp.Set[asyncio.Task] = set() self._pending_jobs: tp.Set[asyncio.Task] = set()
self._starter_task: asyncio.Task self._starter_task: asyncio.Task
def add_run(self, run: Run): def add_run(self, run: Run) -> None:
if run.experiment.resreq_cores() > self.cores: if run.experiment.resreq_cores() > self.cores:
raise RuntimeError('Not enough cores available for run') raise RuntimeError('Not enough cores available for run')
...@@ -119,7 +119,7 @@ class LocalParallelRuntime(Runtime): ...@@ -119,7 +119,7 @@ class LocalParallelRuntime(Runtime):
else: else:
self.runs_prereq.append(run) self.runs_prereq.append(run)
async def do_run(self, run: Run): async def do_run(self, run: Run) -> tp.Optional[Run]:
"""Actually executes `run`.""" """Actually executes `run`."""
try: try:
runner = ExperimentSimpleRunner( runner = ExperimentSimpleRunner(
...@@ -130,7 +130,7 @@ class LocalParallelRuntime(Runtime): ...@@ -130,7 +130,7 @@ class LocalParallelRuntime(Runtime):
except asyncio.CancelledError: except asyncio.CancelledError:
# it is safe to just exit here because we are not running any # it is safe to just exit here because we are not running any
# simulators yet # simulators yet
return return None
print('starting run ', run.name()) print('starting run ', run.name())
run.output = await runner.run() # already handles CancelledError run.output = await runner.run() # already handles CancelledError
...@@ -144,7 +144,7 @@ class LocalParallelRuntime(Runtime): ...@@ -144,7 +144,7 @@ class LocalParallelRuntime(Runtime):
print('finished run ', run.name()) print('finished run ', run.name())
return run return run
async def wait_completion(self): async def wait_completion(self) -> None:
"""Wait for any run to terminate and return.""" """Wait for any run to terminate and return."""
assert self._pending_jobs assert self._pending_jobs
...@@ -158,7 +158,7 @@ class LocalParallelRuntime(Runtime): ...@@ -158,7 +158,7 @@ class LocalParallelRuntime(Runtime):
self.cores_used -= run.experiment.resreq_cores() self.cores_used -= run.experiment.resreq_cores()
self.mem_used -= run.experiment.resreq_mem() self.mem_used -= run.experiment.resreq_mem()
def enough_resources(self, run: Run): def enough_resources(self, run: Run) -> bool:
"""Check if enough cores and mem are available for the run.""" """Check if enough cores and mem are available for the run."""
exp = run.experiment # pylint: disable=redefined-outer-name exp = run.experiment # pylint: disable=redefined-outer-name
...@@ -174,14 +174,14 @@ class LocalParallelRuntime(Runtime): ...@@ -174,14 +174,14 @@ class LocalParallelRuntime(Runtime):
return enough_cores and enough_mem return enough_cores and enough_mem
def prereq_ready(self, run: Run): def prereq_ready(self, run: Run) -> bool:
"""Check if the prerequesite run for `run` has completed.""" """Check if the prerequesite run for `run` has completed."""
if run.prereq is None: if run.prereq is None:
return True return True
return run.prereq in self.complete return run.prereq in self.complete
async def do_start(self): async def do_start(self) -> None:
"""Asynchronously execute the runs defined in `self.runs_noprereq + """Asynchronously execute the runs defined in `self.runs_noprereq +
self.runs_prereq.""" self.runs_prereq."""
#self.completions = asyncio.Queue() #self.completions = asyncio.Queue()
......
...@@ -32,7 +32,7 @@ from simbricks.orchestration.runtime.common import Run, Runtime ...@@ -32,7 +32,7 @@ from simbricks.orchestration.runtime.common import Run, Runtime
class SlurmRuntime(Runtime): class SlurmRuntime(Runtime):
def __init__(self, slurmdir, args, verbose=False, cleanup=True): def __init__(self, slurmdir, args, verbose=False, cleanup=True) -> None:
super().__init__() super().__init__()
self.runnable: tp.List[Run] = [] self.runnable: tp.List[Run] = []
self.slurmdir = slurmdir self.slurmdir = slurmdir
...@@ -42,10 +42,10 @@ class SlurmRuntime(Runtime): ...@@ -42,10 +42,10 @@ class SlurmRuntime(Runtime):
self._start_task: asyncio.Task self._start_task: asyncio.Task
def add_run(self, run: Run): def add_run(self, run: Run) -> None:
self.runnable.append(run) self.runnable.append(run)
def prep_run(self, run: Run): def prep_run(self, run: Run) -> str:
exp = run.experiment exp = run.experiment
e_idx = exp.name + f'-{run.index}' + '.exp' e_idx = exp.name + f'-{run.index}' + '.exp'
exp_path = os.path.join(self.slurmdir, e_idx) exp_path = os.path.join(self.slurmdir, e_idx)
...@@ -92,7 +92,7 @@ class SlurmRuntime(Runtime): ...@@ -92,7 +92,7 @@ class SlurmRuntime(Runtime):
return exp_script return exp_script
async def _do_start(self): async def _do_start(self) -> None:
pathlib.Path(self.slurmdir).mkdir(parents=True, exist_ok=True) pathlib.Path(self.slurmdir).mkdir(parents=True, exist_ok=True)
jid_re = re.compile(r'Submitted batch job ([0-9]+)') jid_re = re.compile(r'Submitted batch job ([0-9]+)')
......
...@@ -41,7 +41,7 @@ def create_basic_hosts( ...@@ -41,7 +41,7 @@ def create_basic_hosts(
app_class: tp.Type[AppConfig], app_class: tp.Type[AppConfig],
ip_start: int = 1, ip_start: int = 1,
ip_prefix: int = 24 ip_prefix: int = 24
): ) -> tp.List[HostSim]:
""" """
Creates and configures multiple hosts to be simulated using the given Creates and configures multiple hosts to be simulated using the given
parameters. parameters.
...@@ -84,7 +84,7 @@ def create_multinic_hosts( ...@@ -84,7 +84,7 @@ def create_multinic_hosts(
app_class: tp.Type[AppConfig], app_class: tp.Type[AppConfig],
ip_start: int = 1, ip_start: int = 1,
ip_prefix: int = 24 ip_prefix: int = 24
): ) -> tp.List[HostSim]:
""" """
Creates and configures multiple hosts to be simulated using the given Creates and configures multiple hosts to be simulated using the given
parameters. These hosts use multiple NICs. parameters. These hosts use multiple NICs.
...@@ -133,7 +133,7 @@ def create_dctcp_hosts( ...@@ -133,7 +133,7 @@ def create_dctcp_hosts(
cpu_freq: str, cpu_freq: str,
mtu: int, mtu: int,
ip_start: int = 1 ip_start: int = 1
): ) -> tp.List[HostSim]:
""" """
Creates and configures multiple hosts to be simulated in a DCTCP experiment Creates and configures multiple hosts to be simulated in a DCTCP experiment
using the given parameters. using the given parameters.
......
...@@ -33,11 +33,11 @@ from simbricks.orchestration.nodeconfig import NodeConfig ...@@ -33,11 +33,11 @@ from simbricks.orchestration.nodeconfig import NodeConfig
class Simulator(object): class Simulator(object):
"""Base class for all simulators.""" """Base class for all simulators."""
def __init__(self): def __init__(self) -> None:
self.extra_deps = [] self.extra_deps: tp.List[Simulator] = []
self.name = '' self.name = ''
def resreq_cores(self): def resreq_cores(self) -> int:
""" """
Number of cores this simulator requires during execution. Number of cores this simulator requires during execution.
...@@ -45,7 +45,7 @@ class Simulator(object): ...@@ -45,7 +45,7 @@ class Simulator(object):
""" """
return 1 return 1
def resreq_mem(self): def resreq_mem(self) -> int:
""" """
Number of memory in MB this simulator requires during execution. Number of memory in MB this simulator requires during execution.
...@@ -53,7 +53,7 @@ class Simulator(object): ...@@ -53,7 +53,7 @@ class Simulator(object):
""" """
return 64 return 64
def full_name(self): def full_name(self) -> str:
"""Full name of the simulator.""" """Full name of the simulator."""
return '' return ''
...@@ -73,25 +73,25 @@ class Simulator(object): ...@@ -73,25 +73,25 @@ class Simulator(object):
# Sockets to be cleaned up # Sockets to be cleaned up
# pylint: disable=unused-argument # pylint: disable=unused-argument
def sockets_cleanup(self, env: ExpEnv): def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
return [] return []
# 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
def sockets_wait(self, env: ExpEnv): def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
return [] return []
def start_delay(self): def start_delay(self) -> int:
return 5 return 5
def wait_terminate(self): def wait_terminate(self) -> bool:
return False return False
class PCIDevSim(Simulator): class PCIDevSim(Simulator):
"""Base class for PCIe device simulators.""" """Base class for PCIe device simulators."""
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.sync_mode = 0 self.sync_mode = 0
...@@ -110,23 +110,23 @@ class PCIDevSim(Simulator): ...@@ -110,23 +110,23 @@ class PCIDevSim(Simulator):
"""Latency in nanoseconds for sending messages to components connected """Latency in nanoseconds for sending messages to components connected
via PCI.""" via PCI."""
def full_name(self): def full_name(self) -> str:
return 'dev.' + self.name return 'dev.' + self.name
def is_nic(self): def is_nic(self) -> bool:
return False return False
def sockets_cleanup(self, env): def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
return [env.dev_pci_path(self), env.dev_shm_path(self)] return [env.dev_pci_path(self), env.dev_shm_path(self)]
def sockets_wait(self, env): def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
return [env.dev_pci_path(self)] return [env.dev_pci_path(self)]
class NICSim(PCIDevSim): class NICSim(PCIDevSim):
"""Base class for NIC simulators.""" """Base class for NIC simulators."""
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.network: tp.Optional[NetSim] = None self.network: tp.Optional[NetSim] = None
...@@ -135,12 +135,12 @@ class NICSim(PCIDevSim): ...@@ -135,12 +135,12 @@ class NICSim(PCIDevSim):
"""Ethernet latency in nanoseconds from this NIC to the network """Ethernet latency in nanoseconds from this NIC to the network
component.""" component."""
def set_network(self, net: NetSim): def set_network(self, net: NetSim) -> None:
"""Connect this NIC to a network simulator.""" """Connect this NIC to a network simulator."""
self.network = net self.network = net
net.nics.append(self) net.nics.append(self)
def basic_args(self, env, extra=None): def basic_args(self, env: ExpEnv, extra: tp.Optional[str] = None) -> str:
cmd = ( cmd = (
f'{env.dev_pci_path(self)} {env.nic_eth_path(self)}' f'{env.dev_pci_path(self)} {env.nic_eth_path(self)}'
f' {env.dev_shm_path(self)} {self.sync_mode} {self.start_tick}' f' {env.dev_shm_path(self)} {self.sync_mode} {self.start_tick}'
...@@ -153,27 +153,29 @@ class NICSim(PCIDevSim): ...@@ -153,27 +153,29 @@ class NICSim(PCIDevSim):
cmd += ' ' + extra cmd += ' ' + extra
return cmd return cmd
def basic_run_cmd(self, env, name, extra=None): def basic_run_cmd(
self, env: ExpEnv, name: str, extra: tp.Optional[str] = None
) -> str:
cmd = f'{env.repodir}/sims/nic/{name} {self.basic_args(env, extra)}' cmd = f'{env.repodir}/sims/nic/{name} {self.basic_args(env, extra)}'
return cmd return cmd
def full_name(self): def full_name(self) -> str:
return 'nic.' + self.name return 'nic.' + self.name
def is_nic(self): def is_nic(self) -> bool:
return True return True
def sockets_cleanup(self, env): def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
return super().sockets_cleanup(env) + [env.nic_eth_path(self)] return super().sockets_cleanup(env) + [env.nic_eth_path(self)]
def sockets_wait(self, env): def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
return super().sockets_wait(env) + [env.nic_eth_path(self)] return super().sockets_wait(env) + [env.nic_eth_path(self)]
class NetSim(Simulator): class NetSim(Simulator):
"""Base class for network simulators.""" """Base class for network simulators."""
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.opt = '' self.opt = ''
...@@ -191,10 +193,10 @@ class NetSim(Simulator): ...@@ -191,10 +193,10 @@ class NetSim(Simulator):
self.net_listen: list[NetSim] = [] self.net_listen: list[NetSim] = []
self.net_connect: list[NetSim] = [] self.net_connect: list[NetSim] = []
def full_name(self): def full_name(self) -> str:
return 'net.' + self.name return 'net.' + self.name
def connect_network(self, net: NetSim): def connect_network(self, net: NetSim) -> None:
"""Connect this network to the listening peer `net`""" """Connect this network to the listening peer `net`"""
net.net_listen.append(self) net.net_listen.append(self)
self.net_connect.append(net) self.net_connect.append(net)
...@@ -209,19 +211,19 @@ class NetSim(Simulator): ...@@ -209,19 +211,19 @@ class NetSim(Simulator):
sockets.append((h, env.net2host_eth_path(self, h))) sockets.append((h, env.net2host_eth_path(self, h)))
return sockets return sockets
def listen_sockets(self, env: ExpEnv): def listen_sockets(self, env: ExpEnv) -> tp.List[tp.Tuple[NetSim, str]]:
listens = [] listens = []
for net in self.net_listen: for net in self.net_listen:
listens.append((net, env.n2n_eth_path(self, net))) listens.append((net, env.n2n_eth_path(self, net)))
return listens return listens
def dependencies(self): def dependencies(self) -> tp.List[Simulator]:
return self.nics + self.net_connect + self.hosts_direct return self.nics + self.net_connect + self.hosts_direct
def sockets_cleanup(self, env: ExpEnv): def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
return [s for (_, s) in self.listen_sockets(env)] return [s for (_, s) in self.listen_sockets(env)]
def sockets_wait(self, env: ExpEnv): def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
return [s for (_, s) in self.listen_sockets(env)] return [s for (_, s) in self.listen_sockets(env)]
...@@ -229,7 +231,7 @@ class NetSim(Simulator): ...@@ -229,7 +231,7 @@ class NetSim(Simulator):
class MemDevSim(NICSim): class MemDevSim(NICSim):
"""Base class for memory device simulators.""" """Base class for memory device simulators."""
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.mem_latency = 500 self.mem_latency = 500
...@@ -237,40 +239,40 @@ class MemDevSim(NICSim): ...@@ -237,40 +239,40 @@ class MemDevSim(NICSim):
self.size = 1024 * 1024 * 1024 # 1GB self.size = 1024 * 1024 * 1024 # 1GB
self.as_id = 0 self.as_id = 0
def full_name(self): def full_name(self) -> str:
return 'mem.' + self.name return 'mem.' + self.name
def sockets_cleanup(self, env): def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
return [env.dev_mem_path(self), env.dev_shm_path(self)] return [env.dev_mem_path(self), env.dev_shm_path(self)]
def sockets_wait(self, env): def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
return [env.dev_mem_path(self)] return [env.dev_mem_path(self)]
class NetMemSim(NICSim): class NetMemSim(NICSim):
"""Base class for netork memory simulators.""" """Base class for netork memory simulators."""
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.addr = 0xe000000000000000 self.addr = 0xe000000000000000
self.size = 1024 * 1024 * 1024 # 1GB self.size = 1024 * 1024 * 1024 # 1GB
self.as_id = 0 self.as_id = 0
def full_name(self): def full_name(self) -> str:
return 'netmem.' + self.name return 'netmem.' + self.name
def sockets_cleanup(self, env): def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
return [env.nic_eth_path(self), env.dev_shm_path(self)] return [env.nic_eth_path(self), env.dev_shm_path(self)]
def sockets_wait(self, env): def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
return [env.nic_eth_path(self)] return [env.nic_eth_path(self)]
class HostSim(Simulator): class HostSim(Simulator):
"""Base class for host simulators.""" """Base class for host simulators."""
def __init__(self, node_config: NodeConfig): def __init__(self, node_config: NodeConfig) -> None:
super().__init__() super().__init__()
self.node_config = node_config self.node_config = node_config
"""System configuration for this simulated host. """ """System configuration for this simulated host. """
...@@ -301,31 +303,35 @@ class HostSim(Simulator): ...@@ -301,31 +303,35 @@ class HostSim(Simulator):
self.memdevs: tp.List[MemDevSim] = [] self.memdevs: tp.List[MemDevSim] = []
@property @property
def nics(self): def nics(self) -> tp.List[NICSim]:
return filter(lambda pcidev: pcidev.is_nic(), self.pcidevs) return [
tp.cast(NICSim, pcidev)
for pcidev in self.pcidevs
if pcidev.is_nic()
]
def full_name(self): def full_name(self) -> str:
return 'host.' + self.name return 'host.' + self.name
def add_nic(self, dev: NICSim): def add_nic(self, dev: NICSim) -> None:
"""Add a NIC to this host.""" """Add a NIC to this host."""
self.add_pcidev(dev) self.add_pcidev(dev)
def add_pcidev(self, dev: PCIDevSim): def add_pcidev(self, dev: PCIDevSim) -> None:
"""Add a PCIe device to this host.""" """Add a PCIe device to this host."""
dev.name = self.name + '.' + dev.name dev.name = self.name + '.' + dev.name
self.pcidevs.append(dev) self.pcidevs.append(dev)
def add_memdev(self, dev: MemDevSim): def add_memdev(self, dev: MemDevSim) -> None:
dev.name = self.name + '.' + dev.name dev.name = self.name + '.' + dev.name
self.memdevs.append(dev) self.memdevs.append(dev)
def add_netdirect(self, net: NetSim): def add_netdirect(self, net: NetSim) -> None:
"""Add a direct connection to a network to this host.""" """Add a direct connection to a network to this host."""
net.hosts_direct.append(self) net.hosts_direct.append(self)
self.net_directs.append(net) self.net_directs.append(net)
def dependencies(self): def dependencies(self) -> tp.List[PCIDevSim]:
deps = [] deps = []
for dev in self.pcidevs: for dev in self.pcidevs:
deps.append(dev) deps.append(dev)
...@@ -335,36 +341,36 @@ class HostSim(Simulator): ...@@ -335,36 +341,36 @@ class HostSim(Simulator):
deps.append(dev) deps.append(dev)
return deps return deps
def wait_terminate(self): def wait_terminate(self) -> bool:
return self.wait return self.wait
class QemuHost(HostSim): class QemuHost(HostSim):
"""Qemu host simulator.""" """Qemu host simulator."""
def __init__(self, node_config: NodeConfig): def __init__(self, node_config: NodeConfig) -> None:
super().__init__(node_config) super().__init__(node_config)
self.sync = False self.sync = False
""""Whether to synchronize with attached simulators.""" """"Whether to synchronize with attached simulators."""
def resreq_cores(self): def resreq_cores(self) -> int:
if self.sync: if self.sync:
return 1 return 1
else: else:
return self.node_config.cores + 1 return self.node_config.cores + 1
def resreq_mem(self): def resreq_mem(self) -> int:
return 8192 return 8192
def prep_cmds(self, env): def prep_cmds(self, env: ExpEnv) -> tp.List[str]:
return [ return [
f'{env.qemu_img_path} create -f qcow2 -o ' f'{env.qemu_img_path} create -f qcow2 -o '
f'backing_file="{env.hd_path(self.node_config.disk_image)}" ' f'backing_file="{env.hd_path(self.node_config.disk_image)}" '
f'{env.hdcopy_path(self)}' f'{env.hdcopy_path(self)}'
] ]
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
accel = ',accel=kvm:tcg' if not self.sync else '' accel = ',accel=kvm:tcg' if not self.sync else ''
if self.node_config.kcmd_append: if self.node_config.kcmd_append:
kcmd_append = ' ' + self.node_config.kcmd_append kcmd_append = ' ' + self.node_config.kcmd_append
...@@ -416,7 +422,7 @@ class QemuHost(HostSim): ...@@ -416,7 +422,7 @@ class QemuHost(HostSim):
class Gem5Host(HostSim): class Gem5Host(HostSim):
"""Gem5 host simulator.""" """Gem5 host simulator."""
def __init__(self, node_config: NodeConfig): def __init__(self, node_config: NodeConfig) -> None:
node_config.sim = 'gem5' node_config.sim = 'gem5'
super().__init__(node_config) super().__init__(node_config)
self.cpu_type_cp = 'X86KvmCPU' self.cpu_type_cp = 'X86KvmCPU'
...@@ -426,16 +432,16 @@ class Gem5Host(HostSim): ...@@ -426,16 +432,16 @@ class Gem5Host(HostSim):
self.extra_config_args = [] self.extra_config_args = []
self.variant = 'fast' self.variant = 'fast'
def resreq_cores(self): def resreq_cores(self) -> int:
return 1 return 1
def resreq_mem(self): def resreq_mem(self) -> int:
return 4096 return 4096
def prep_cmds(self, env): def prep_cmds(self, env: ExpEnv) -> tp.List[str]:
return [f'mkdir -p {env.gem5_cpdir(self)}'] return [f'mkdir -p {env.gem5_cpdir(self)}']
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
cpu_type = self.cpu_type cpu_type = self.cpu_type
if env.create_cp: if env.create_cp:
cpu_type = self.cpu_type_cp cpu_type = self.cpu_type_cp
...@@ -506,7 +512,7 @@ class Gem5Host(HostSim): ...@@ -506,7 +512,7 @@ class Gem5Host(HostSim):
class SimicsHost(HostSim): class SimicsHost(HostSim):
"""Simics host simulator.""" """Simics host simulator."""
def __init__(self, node_config: NodeConfig): def __init__(self, node_config: NodeConfig) -> None:
super().__init__(node_config) super().__init__(node_config)
node_config.sim = 'simics' node_config.sim = 'simics'
...@@ -527,13 +533,13 @@ class SimicsHost(HostSim): ...@@ -527,13 +533,13 @@ class SimicsHost(HostSim):
self.debug_messages = False self.debug_messages = False
"""Whether to enable debug messages of SimBricks adapter devices.""" """Whether to enable debug messages of SimBricks adapter devices."""
def resreq_cores(self): def resreq_cores(self) -> int:
return 2 return 2
def resreq_mem(self): def resreq_mem(self) -> int:
return self.node_config.memory return self.node_config.memory
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
if self.node_config.kcmd_append: if self.node_config.kcmd_append:
raise RuntimeError( raise RuntimeError(
'Appending kernel command-line not yet implemented.' 'Appending kernel command-line not yet implemented.'
...@@ -670,15 +676,15 @@ class SimicsHost(HostSim): ...@@ -670,15 +676,15 @@ class SimicsHost(HostSim):
class CorundumVerilatorNIC(NICSim): class CorundumVerilatorNIC(NICSim):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.clock_freq = 250 # MHz self.clock_freq = 250 # MHz
def resreq_mem(self): def resreq_mem(self) -> int:
# this is a guess # this is a guess
return 512 return 512
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
return self.basic_run_cmd( return self.basic_run_cmd(
env, '/corundum/corundum_verilator', str(self.clock_freq) env, '/corundum/corundum_verilator', str(self.clock_freq)
) )
...@@ -686,23 +692,23 @@ class CorundumVerilatorNIC(NICSim): ...@@ -686,23 +692,23 @@ class CorundumVerilatorNIC(NICSim):
class CorundumBMNIC(NICSim): class CorundumBMNIC(NICSim):
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
return self.basic_run_cmd(env, '/corundum_bm/corundum_bm') return self.basic_run_cmd(env, '/corundum_bm/corundum_bm')
class I40eNIC(NICSim): class I40eNIC(NICSim):
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
return self.basic_run_cmd(env, '/i40e_bm/i40e_bm') return self.basic_run_cmd(env, '/i40e_bm/i40e_bm')
class E1000NIC(NICSim): class E1000NIC(NICSim):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.debug = False self.debug = False
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
cmd = self.basic_run_cmd(env, '/e1000_gem5/e1000_gem5') cmd = self.basic_run_cmd(env, '/e1000_gem5/e1000_gem5')
if self.debug: if self.debug:
cmd = 'env E1000_DEBUG=1 ' + cmd cmd = 'env E1000_DEBUG=1 ' + cmd
...@@ -711,35 +717,35 @@ class E1000NIC(NICSim): ...@@ -711,35 +717,35 @@ class E1000NIC(NICSim):
class MultiSubNIC(NICSim): class MultiSubNIC(NICSim):
def __init__(self, mn): def __init__(self, mn: Simulator) -> None:
super().__init__() super().__init__()
self.multinic = mn self.multinic = mn
def full_name(self): def full_name(self) -> str:
return self.multinic.full_name() + '.' + self.name return self.multinic.full_name() + '.' + self.name
def dependencies(self): def dependencies(self) -> tp.List[Simulator]:
return super().dependencies() + [self.multinic] return super().dependencies() + [self.multinic]
def start_delay(self): def start_delay(self) -> int:
return 0 return 0
class I40eMultiNIC(Simulator): class I40eMultiNIC(Simulator):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.subnics = [] self.subnics: tp.List[NICSim] = []
def create_subnic(self): def create_subnic(self) -> MultiSubNIC:
sn = MultiSubNIC(self) sn = MultiSubNIC(self)
self.subnics.append(sn) self.subnics.append(sn)
return sn return sn
def full_name(self): def full_name(self) -> str:
return 'multinic.' + self.name return 'multinic.' + self.name
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
args = '' args = ''
first = True first = True
for sn in self.subnics: for sn in self.subnics:
...@@ -749,13 +755,13 @@ class I40eMultiNIC(Simulator): ...@@ -749,13 +755,13 @@ class I40eMultiNIC(Simulator):
args += sn.basic_args(env) args += sn.basic_args(env)
return f'{env.repodir}/sims/nic/i40e_bm/i40e_bm {args}' return f'{env.repodir}/sims/nic/i40e_bm/i40e_bm {args}'
def sockets_cleanup(self, env): def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
ss = [] ss = []
for sn in self.subnics: for sn in self.subnics:
ss += sn.sockets_cleanup(env) ss += sn.sockets_cleanup(env)
return ss return ss
def sockets_wait(self, env): def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
ss = [] ss = []
for sn in self.subnics: for sn in self.subnics:
ss += sn.sockets_wait(env) ss += sn.sockets_wait(env)
...@@ -764,7 +770,7 @@ class I40eMultiNIC(Simulator): ...@@ -764,7 +770,7 @@ class I40eMultiNIC(Simulator):
class WireNet(NetSim): class WireNet(NetSim):
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
connects = self.connect_sockets(env) connects = self.connect_sockets(env)
assert len(connects) == 2 assert len(connects) == 2
cmd = ( cmd = (
...@@ -779,12 +785,12 @@ class WireNet(NetSim): ...@@ -779,12 +785,12 @@ class WireNet(NetSim):
class SwitchNet(NetSim): class SwitchNet(NetSim):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.sync = True self.sync = True
"""Whether to synchronize with attached simulators.""" """Whether to synchronize with attached simulators."""
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
cmd = env.repodir + '/sims/net/switch/net_switch' cmd = env.repodir + '/sims/net/switch/net_switch'
cmd += f' -S {self.sync_period} -E {self.eth_latency}' cmd += f' -S {self.sync_period} -E {self.eth_latency}'
...@@ -799,7 +805,7 @@ class SwitchNet(NetSim): ...@@ -799,7 +805,7 @@ class SwitchNet(NetSim):
cmd += ' -h ' + n cmd += ' -h ' + n
return cmd return cmd
def sockets_cleanup(self, env): def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
# cleanup here will just have listening eth sockets, switch also creates # cleanup here will just have listening eth sockets, switch also creates
# shm regions for each with a "-shm" suffix # shm regions for each with a "-shm" suffix
cleanup = [] cleanup = []
...@@ -811,13 +817,13 @@ class SwitchNet(NetSim): ...@@ -811,13 +817,13 @@ class SwitchNet(NetSim):
class MemSwitchNet(NetSim): class MemSwitchNet(NetSim):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.sync = True self.sync = True
""" 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 = []
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
cmd = env.repodir + '/sims/mem/memswitch/memswitch' cmd = env.repodir + '/sims/mem/memswitch/memswitch'
cmd += f' -S {self.sync_period} -E {self.eth_latency}' cmd += f' -S {self.sync_period} -E {self.eth_latency}'
...@@ -836,7 +842,7 @@ class MemSwitchNet(NetSim): ...@@ -836,7 +842,7 @@ class MemSwitchNet(NetSim):
cmd += f',{m[4]}' cmd += f',{m[4]}'
return cmd return cmd
def sockets_cleanup(self, env): def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
# cleanup here will just have listening eth sockets, switch also creates # cleanup here will just have listening eth sockets, switch also creates
# shm regions for each with a "-shm" suffix # shm regions for each with a "-shm" suffix
cleanup = [] cleanup = []
...@@ -848,12 +854,12 @@ class MemSwitchNet(NetSim): ...@@ -848,12 +854,12 @@ class MemSwitchNet(NetSim):
class TofinoNet(NetSim): class TofinoNet(NetSim):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.tofino_log_path = '/tmp/model.ldjson' self.tofino_log_path = '/tmp/model.ldjson'
self.sync = True self.sync = True
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
cmd = f'{env.repodir}/sims/net/tofino/tofino' cmd = f'{env.repodir}/sims/net/tofino/tofino'
cmd += ( cmd += (
f' -S {self.sync_period} -E {self.eth_latency}' f' -S {self.sync_period} -E {self.eth_latency}'
...@@ -868,7 +874,7 @@ class TofinoNet(NetSim): ...@@ -868,7 +874,7 @@ class TofinoNet(NetSim):
class NS3DumbbellNet(NetSim): class NS3DumbbellNet(NetSim):
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
ports = '' ports = ''
for (n, s) in self.connect_sockets(env): for (n, s) in self.connect_sockets(env):
if 'server' in n.name: if 'server' in n.name:
...@@ -887,7 +893,7 @@ class NS3DumbbellNet(NetSim): ...@@ -887,7 +893,7 @@ class NS3DumbbellNet(NetSim):
class NS3BridgeNet(NetSim): class NS3BridgeNet(NetSim):
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
ports = '' ports = ''
for (_, n) in self.connect_sockets(env): for (_, n) in self.connect_sockets(env):
ports += '--CosimPort=' + n + ' ' ports += '--CosimPort=' + n + ' '
...@@ -903,7 +909,7 @@ class NS3BridgeNet(NetSim): ...@@ -903,7 +909,7 @@ class NS3BridgeNet(NetSim):
class NS3SequencerNet(NetSim): class NS3SequencerNet(NetSim):
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
ports = '' ports = ''
for (n, s) in self.connect_sockets(env): for (n, s) in self.connect_sockets(env):
if 'client' in n.name: if 'client' in n.name:
...@@ -924,7 +930,7 @@ class NS3SequencerNet(NetSim): ...@@ -924,7 +930,7 @@ class NS3SequencerNet(NetSim):
class FEMUDev(PCIDevSim): class FEMUDev(PCIDevSim):
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
cmd = ( cmd = (
f'{env.repodir}/sims/external/femu/femu-simbricks' f'{env.repodir}/sims/external/femu/femu-simbricks'
f' {env.dev_pci_path(self)} {env.dev_shm_path(self)}' f' {env.dev_pci_path(self)} {env.dev_shm_path(self)}'
...@@ -934,7 +940,7 @@ class FEMUDev(PCIDevSim): ...@@ -934,7 +940,7 @@ class FEMUDev(PCIDevSim):
class BasicMemDev(MemDevSim): class BasicMemDev(MemDevSim):
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
cmd = ( cmd = (
f'{env.repodir}/sims/mem/basicmem/basicmem' f'{env.repodir}/sims/mem/basicmem/basicmem'
f' {self.size} {self.addr} {self.as_id}' f' {self.size} {self.addr} {self.as_id}'
...@@ -947,7 +953,7 @@ class BasicMemDev(MemDevSim): ...@@ -947,7 +953,7 @@ class BasicMemDev(MemDevSim):
class MemNIC(MemDevSim): class MemNIC(MemDevSim):
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
cmd = ( cmd = (
f'{env.repodir}/sims/mem/memnic/memnic' f'{env.repodir}/sims/mem/memnic/memnic'
f' {env.dev_mem_path(self)} {env.nic_eth_path(self)}' f' {env.dev_mem_path(self)} {env.nic_eth_path(self)}'
...@@ -962,16 +968,16 @@ class MemNIC(MemDevSim): ...@@ -962,16 +968,16 @@ class MemNIC(MemDevSim):
return cmd return cmd
def sockets_cleanup(self, env): def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
return super().sockets_cleanup(env) + [env.nic_eth_path(self)] return super().sockets_cleanup(env) + [env.nic_eth_path(self)]
def sockets_wait(self, env): def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
return super().sockets_wait(env) + [env.nic_eth_path(self)] return super().sockets_wait(env) + [env.nic_eth_path(self)]
class NetMem(NetMemSim): class NetMem(NetMemSim):
def run_cmd(self, env): def run_cmd(self, env: ExpEnv) -> str:
cmd = ( cmd = (
f'{env.repodir}/sims/mem/netmem/netmem' f'{env.repodir}/sims/mem/netmem/netmem'
f' {self.size} {self.addr} {self.as_id}' f' {self.size} {self.addr} {self.as_id}'
......
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