Commit cd155cd4 authored by Hejing Li's avatar Hejing Li Committed by Jonas Kaufmann
Browse files

simple_ping script runs

parent 7bb0dbb0
import simbricks.orchestration.experiments as exp
import simbricks.splitsim.specification as spec import simbricks.splitsim.specification as spec
import simbricks.splitsim.impl as impl
""" """
Simple Ping Example: Simple Ping Example:
...@@ -36,4 +38,27 @@ ethchannel1 = spec.Eth(system) ...@@ -36,4 +38,27 @@ ethchannel1 = spec.Eth(system)
ethchannel1.install(nic1, port1) ethchannel1.install(nic1, port1)
# configure the software to run on the host # configure the software to run on the host
host0.app = spec.PingClient('10.0.0.2') host0.app = spec.PingClient('10.0.0.2')
\ No newline at end of file host1.app = spec.Sleep()
"""
Execution Config
"""
experiments = []
e = exp.Experiment('simple_ping_sysconf')
host_inst0 = impl.Gem5Sim(e)
host_inst0.add(system.hosts[0])
host_inst1 = impl.Gem5Sim(e)
host_inst1.add(system.hosts[1])
nic_inst0 = impl.I40eNicSim(e)
nic_inst0.add(system.nics[0])
nic_inst1 = impl.I40eNicSim(e)
nic_inst1.add(system.nics[1])
net_inst = impl.SwitchBMSim(e)
net_inst.add(system.switches[0])
experiments.append(e)
\ No newline at end of file
# Copyright 2021 Max Planck Institute for Software Systems, and
# National University of Singapore
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import os
import typing as tp
if tp.TYPE_CHECKING: # prevent cyclic import
# from simbricks.orchestration import simulators
from simbricks.splitsim import impl as simulators
class ExpEnv(object):
"""Manages the experiment environment."""
def __init__(self, repo_path: str, workdir: str, cpdir: str) -> None:
self.create_cp = False
"""Whether a checkpoint should be created."""
self.restore_cp = False
"""Whether to restore from a checkpoint."""
self.pcap_file = ''
self.repodir = os.path.abspath(repo_path)
self.workdir = os.path.abspath(workdir)
self.cpdir = os.path.abspath(cpdir)
self.shm_base = self.workdir
self.utilsdir = f'{self.repodir}/experiments/simbricks/utils'
"""Directory containing some utility scripts/binaries."""
self.qemu_img_path = f'{self.repodir}/sims/external/qemu/build/qemu-img'
self.qemu_path = (
f'{self.repodir}/sims/external/qemu/build/'
'x86_64-softmmu/qemu-system-x86_64'
)
self.qemu_kernel_path = f'{self.repodir}/images/bzImage'
self.gem5_py_path = (
f'{self.repodir}/sims/external/gem5/configs/simbricks/simbricks.py'
)
self.gem5_kernel_path = f'{self.repodir}/images/vmlinux'
simics_project_base = f'{self.repodir}/sims/external/simics/project'
self.simics_path = f'{simics_project_base}/simics'
self.simics_gui_path = f'{simics_project_base}/simics-gui'
self.simics_qsp_modern_core_path = (
f'{simics_project_base}/targets/qsp-x86/qsp-modern-core.simics'
)
def gem5_path(self, variant: str) -> str:
return f'{self.repodir}/sims/external/gem5/build/X86/gem5.{variant}'
def hdcopy_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.workdir}/hdcopy.{sim.name}'
def hd_path(self, hd_name: str) -> str:
return f'{self.repodir}/images/output-{hd_name}/{hd_name}'
def hd_raw_path(self, hd_name: str) -> str:
return f'{self.repodir}/images/output-{hd_name}/{hd_name}.raw'
def cfgtar_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.workdir}/cfg.{sim.name}.tar'
def dev_pci_path(self, sim) -> str:
return f'{self.workdir}/dev.pci.{sim.name}'
def dev_mem_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.workdir}/dev.mem.{sim.name}'
def nic_eth_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.workdir}/nic.eth.{sim.name}'
def dev_shm_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.shm_base}/dev.shm.{sim.name}'
def n2n_eth_path(
self, sim_l: 'simulators.Simulator', sim_c: 'simulators.Simulator',
suffix=''
) -> str:
return f'{self.workdir}/n2n.eth.{sim_l.name}.{sim_c.name}.{suffix}'
def net2host_eth_path(self, sim_n, sim_h) -> str:
return f'{self.workdir}/n2h.eth.{sim_n.name}.{sim_h.name}'
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}'
def proxy_shm_path(self, sim: 'simulators.Simulator') -> str:
return f'{self.shm_base}/proxy.shm.{sim.name}'
def gem5_outdir(self, sim: 'simulators.Simulator') -> str:
return f'{self.workdir}/gem5-out.{sim.name}'
def gem5_cpdir(self, sim: 'simulators.Simulator') -> str:
return f'{self.cpdir}/gem5-cp.{sim.name}'
def simics_cpfile(self, sim: 'simulators.Simulator') -> str:
return f'{self.cpdir}/simics-cp.{sim.name}'
def ns3_e2e_params_file(self, sim: 'simulators.NS3E2ENet') -> str:
return f'{self.workdir}/ns3_e2e_params.{sim.name}'
import io
import typing as tp
import itertools
import simbricks.splitsim.specification as spec
import simbricks.orchestration.experiments as exp
from simbricks.orchestration.experiment.experiment_environment_new import ExpEnv
class Simulator(object):
"""Base class for all simulators."""
def __init__(self) -> None:
self.extra_deps: tp.List[Simulator] = []
self.name = ''
def resreq_cores(self) -> int:
"""
Number of cores this simulator requires during execution.
This is used for scheduling multiple runs and experiments.
"""
return 1
def resreq_mem(self) -> int:
"""
Number of memory in MB this simulator requires during execution.
This is used for scheduling multiple runs and experiments.
"""
return 64
def full_name(self) -> str:
"""Full name of the simulator."""
return ''
# pylint: disable=unused-argument
def prep_cmds(self, env: ExpEnv) -> tp.List[str]:
"""Commands to prepare execution of this simulator."""
return []
# pylint: disable=unused-argument
def run_cmd(self, env: ExpEnv) -> tp.Optional[str]:
"""Command to execute this simulator."""
return None
def dependencies(self):
"""Other simulators to execute before this one."""
return []
# Sockets to be cleaned up
# pylint: disable=unused-argument
def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
return []
# sockets to wait for indicating the simulator is ready
# pylint: disable=unused-argument
def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
return []
def start_delay(self) -> int:
return 5
def wait_terminate(self) -> bool:
return False
class Gem5Sim(Simulator):
def __init__(self, e: exp.Experiment):
super().__init__()
self.experiment = e
self.hosts: tp.List[spec.Host] = []
self.nics: tp.List[spec.NIC] = []
# move it to add
self.sync_period = 500
"""Period in nanoseconds of sending synchronization messages from this
device to connected components."""
self.pci_latency = 500
self.name = ''
self.cpu_type_cp = 'X86KvmCPU'
self.cpu_type = 'TimingSimpleCPU'
self.extra_main_args = []
self.extra_config_args = []
self.variant = 'fast'
self.modify_checkpoint_tick = True
self.wait = True
def full_name(self) -> str:
return 'host.' + self.name
def resreq_cores(self) -> int:
return 1
def resreq_mem(self) -> int:
return 4096
def dependencies(self) -> tp.List[Simulator]:
deps = []
for h in self.hosts:
for dev in h.nics:
deps.append(dev.sim)
return deps
def add(self, host: spec.Host):
self.hosts.append(host)
host.sim = 'gem5'
self.name = f'{self.hosts[0].id}'
self.experiment.add_host(self)
def prep_cmds(self, env: ExpEnv) -> tp.List[str]:
cmds = [f'mkdir -p {env.gem5_cpdir(self)}']
if env.restore_cp and self.modify_checkpoint_tick:
cmds.append(
f'python3 {env.utilsdir}/modify_gem5_cp_tick.py --tick 0 '
f'--cpdir {env.gem5_cpdir(self)}'
)
return cmds
def run_cmd(self, env: ExpEnv) -> str:
cpu_type = self.cpu_type
if env.create_cp:
cpu_type = self.cpu_type_cp
cmd = f'{env.gem5_path(self.variant)} --outdir={env.gem5_outdir(self)} '
cmd += ' '.join(self.extra_main_args)
cmd += (
f' {env.gem5_py_path} --caches --l2cache '
'--l1d_size=32kB --l1i_size=32kB --l2_size=32MB '
'--l1d_assoc=8 --l1i_assoc=8 --l2_assoc=16 '
f'--cacheline_size=64 --cpu-clock={self.hosts[0].cpu_freq}'
f' --sys-clock={self.hosts[0].sys_clock} '
f'--checkpoint-dir={env.gem5_cpdir(self)} '
f'--kernel={env.gem5_kernel_path} '
f'--disk-image={env.hd_raw_path(self.hosts[0].disk_image)} '
f'--disk-image={env.cfgtar_path(self)} '
f'--cpu-type={cpu_type} --mem-size={self.hosts[0].memory}MB '
f'--num-cpus={self.hosts[0].cores} '
'--mem-type=DDR4_2400_16x4 '
)
for dev in self.nics:
cmd += (
f'--simbricks-pci=connect:{env.dev_pci_path(dev)}'
f':latency={self.pci_latency}ns'
f':sync_interval={self.sync_period}ns'
)
if cpu_type == 'TimingSimpleCPU':
cmd += ':sync'
cmd += ' '
return cmd
def wait_terminate(self) -> bool:
return self.wait
class I40eNicSim(Simulator):
def __init__(self, e: exp.Experiment):
super().__init__()
self.experiment = e
self.nics: tp.List[spec.i40eNIC] = []
self.start_tick = 0
def add(self, nic: spec.i40eNIC):
self.nics.append(nic)
nic.sim = self
self.experiment.add_nic(self)
self.name = f'{nic.id}'
def basic_args(self, env: ExpEnv, extra: tp.Optional[str] = None) -> str:
cmd = (
f'{env.dev_pci_path(self)} {env.nic_eth_path(self)}'
f' {env.dev_shm_path(self)} {self.nics[0].sync} {self.start_tick}'
f' {self.nics[0].eth_channel.sync_period} {self.nics[0].pci_channel.latency} {self.nics[0].eth_channel.latency}'
)
if self.nics[0].mac is not None:
cmd += ' ' + (''.join(reversed(self.nics[0].mac.split(':'))))
if extra is not None:
cmd += ' ' + extra
return cmd
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)}'
return cmd
def full_name(self) -> str:
return 'nic.' + self.name
def is_nic(self) -> bool:
return True
def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
return super().sockets_cleanup(env) + [env.nic_eth_path(self)]
def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
return super().sockets_wait(env) + [env.nic_eth_path(self)]
def run_cmd(self, env: ExpEnv) -> str:
return self.basic_run_cmd(env, '/i40e_bm/i40e_bm')
class SwitchBMSim(Simulator):
def __init__(self, e: exp.Experiment):
super().__init__()
self.experiment = e
self.switches: tp.List[spec.Switch] = []
self.nicSim: tp.List[I40eNicSim] = []
self.wait = False
def full_name(self) -> str:
return 'net.' + self.name
def add(self, switch: spec.Switch):
self.switches.append(switch)
switch.sim = self
self.experiment.add_network(self)
for s in self.switches:
for n in s.netdevs:
self.nicSim.append(n.net[0].sim)
def connect_sockets(self, env: ExpEnv) -> tp.List[tp.Tuple[Simulator, str]]:
sockets = []
for n in self.nicSim:
sockets.append((n, env.nic_eth_path(n)))
return sockets
def dependencies(self) -> tp.List[Simulator]:
deps = []
for s in self.switches:
for n in s.netdevs:
deps.append(n.net[0].sim)
return deps
def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
pass
def sockets_wait(self, env: ExpEnv) -> tp.List[str]:
pass
def wait_terminate(self) -> bool:
return self.wait
def init_network(self) -> None:
pass
def sockets_cleanup(self, env: ExpEnv) -> tp.List[str]:
cleanup = []
return cleanup
def run_cmd(self, env: ExpEnv) -> str:
cmd = env.repodir + '/sims/net/switch/net_switch'
cmd += f' -S {self.switches[0].sync_period} -E {self.switches[0].eth_latency}'
if not self.switches[0].sync:
cmd += ' -u'
if len(env.pcap_file) > 0:
cmd += ' -p ' + env.pcap_file
for (_, n) in self.connect_sockets(env):
cmd += ' -s ' + n
# for (_, n) in self.listen_sockets(env):
# cmd += ' -h ' + n
return cmd
\ No newline at end of file
import io import io
import typing as tp import typing as tp
import tarfile
import itertools import itertools
class System(): class System():
...@@ -12,9 +13,11 @@ class System(): ...@@ -12,9 +13,11 @@ class System():
self.pci_channels: tp.List[PCI] = [] self.pci_channels: tp.List[PCI] = []
self.eth_channels: tp.List[Eth] = [] self.eth_channels: tp.List[Eth] = []
class Channel(): class Channel():
def __init__(self) -> None: def __init__(self) -> None:
self.latency = 500 # nano second self.latency = 500 # nano second
self.sync_period = 500 # nano second
def install(self, end_point0, end_point1) -> None: def install(self, end_point0, end_point1) -> None:
return return
...@@ -60,6 +63,7 @@ class Host(): ...@@ -60,6 +63,7 @@ class Host():
self.pci_channel: PCI = None self.pci_channel: PCI = None
self.nics: tp.List[NIC] = [] self.nics: tp.List[NIC] = []
self.nic_driver = 'i40e' self.nic_driver = 'i40e'
self.sim = None
# HostSim & NodeConfig parameters # HostSim & NodeConfig parameters
self.cpu_freq = '3GHz' self.cpu_freq = '3GHz'
...@@ -77,11 +81,109 @@ class Host(): ...@@ -77,11 +81,109 @@ class Host():
"""Name of disk image to use.""" """Name of disk image to use."""
self.mtu = 1500 self.mtu = 1500
"""Networking MTU.""" """Networking MTU."""
self.sys_clock = '1GHz'
"""system bus clock"""
self.tcp_congestion_control = 'bic' self.tcp_congestion_control = 'bic'
"""TCP Congestion Control algorithm to use.""" """TCP Congestion Control algorithm to use."""
self.app: tp.Optional[AppConfig] = None self.app: tp.Optional[AppConfig] = None
"""Application to run on simulated host.""" """Application to run on simulated host."""
self.nockp = 0
"""Do not create a checkpoint in Gem5."""
def config_str(self) -> str:
if self.sim == 'gem5':
cp_es = [] if self.nockp else ['m5 checkpoint']
exit_es = ['m5 exit']
else:
cp_es = ['echo ready to checkpoint']
exit_es = ['poweroff -f']
es = self.prepare_pre_cp() + self.app.prepare_pre_cp(self) + cp_es + \
self.prepare_post_cp() + self.app.prepare_post_cp(self) + \
self.run_cmds() + self.cleanup_cmds() + exit_es
return '\n'.join(es)
def make_tar(self, path: str) -> None:
with tarfile.open(path, 'w:') as tar:
# add main run script
cfg_i = tarfile.TarInfo('guest/run.sh')
cfg_i.mode = 0o777
cfg_f = self.strfile(self.config_str())
cfg_f.seek(0, io.SEEK_END)
cfg_i.size = cfg_f.tell()
cfg_f.seek(0, io.SEEK_SET)
tar.addfile(tarinfo=cfg_i, fileobj=cfg_f)
cfg_f.close()
# add additional config files
# for (n, f) in self.config_files().items():
# f_i = tarfile.TarInfo('guest/' + n)
# f_i.mode = 0o777
# f.seek(0, io.SEEK_END)
# f_i.size = f.tell()
# f.seek(0, io.SEEK_SET)
# tar.addfile(tarinfo=f_i, fileobj=f)
# f.close()
def run_cmds(self) -> tp.List[str]:
"""Commands to run on node."""
return self.app.run_cmds(self)
def cleanup_cmds(self) -> tp.List[str]:
"""Commands to run to cleanup node."""
return []
def prepare_pre_cp(self) -> tp.List[str]:
"""Commands to run to prepare node before checkpointing."""
return [
'set -x',
'export HOME=/root',
'export LANG=en_US',
'export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:' + \
'/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"'
]
def prepare_post_cp(self) -> tp.List[str]:
"""Commands to run to prepare node after checkpoint restore."""
return []
def strfile(self, s: str) -> io.BytesIO:
"""
Helper function to convert a string to an IO handle for usage in
`config_files()`.
Using this, you can create a file with the string as its content on the
simulated node.
"""
return io.BytesIO(bytes(s, encoding='UTF-8'))
class LinuxHost(Host):
def __init__(self, sys) -> None:
super().__init__(sys)
self.ifname = 'eth0'
self.drivers: tp.List[str] = []
self.force_mac_addr: tp.Optional[str] = None
def prepare_post_cp(self) -> tp.List[str]:
l = []
for d in self.drivers:
if d[0] == '/':
l.append('insmod ' + d)
else:
l.append('modprobe ' + d)
if self.force_mac_addr:
l.append(
'ip link set dev ' + self.ifname + ' address ' +
self.force_mac_addr
)
l.append('ip link set dev ' + self.ifname + ' up')
l.append(f'ip addr add {self.ip}/{self.prefix} dev {self.ifname}')
return super().prepare_post_cp() + l
""" """
Pci device NIC Pci device NIC
...@@ -99,7 +201,8 @@ class NIC(): ...@@ -99,7 +201,8 @@ class NIC():
self.mac: tp.Optional[str] = None self.mac: tp.Optional[str] = None
self.host: tp.List[Host] = [] self.host: tp.List[Host] = []
self.net = [] # NIC or NetDev connected through eth channel self.net = [] # NIC or NetDev connected through eth channel
self.sim = None
class i40eNIC(NIC): class i40eNIC(NIC):
def __init__(self, sys) -> None: def __init__(self, sys) -> None:
...@@ -121,6 +224,8 @@ class NetDev(): ...@@ -121,6 +224,8 @@ class NetDev():
self.mac: tp.Optional[str] = None self.mac: tp.Optional[str] = None
self.ip: tp.Optional[str] = None self.ip: tp.Optional[str] = None
self.net = [] # NIC or NetDev connected through eth channel self.net = [] # NIC or NetDev connected through eth channel
self.sim = None
class Switch(): class Switch():
id_iter = itertools.count() id_iter = itertools.count()
...@@ -128,7 +233,13 @@ class Switch(): ...@@ -128,7 +233,13 @@ class Switch():
self.id = next(self.id_iter) self.id = next(self.id_iter)
sys.switches.append(self) sys.switches.append(self)
self.sync = True self.sync = True
# these two: set it when install the channel
self.sync_period = 500 # ns second
self.eth_latency = 500 # ns second
###
self.netdevs : tp.List[NetDev] = [] self.netdevs : tp.List[NetDev] = []
self.sim = None
def install_netdev(self, netdev: NetDev): def install_netdev(self, netdev: NetDev):
self.netdevs.append(netdev) self.netdevs.append(netdev)
...@@ -184,3 +295,11 @@ class PingClient(AppConfig): ...@@ -184,3 +295,11 @@ class PingClient(AppConfig):
### add commands for dummy Hosts here ### add commands for dummy Hosts here
class Sleep(AppConfig):
def __init__(self, server_ip: str = '192.168.64.2') -> None:
super().__init__()
self.server_ip = server_ip
def run_cmds(self, node: Host) -> tp.List[str]:
return ['sleep']
\ No newline at end of file
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