Commit a5a1ff28 authored by Antoine Kaufmann's avatar Antoine Kaufmann
Browse files

tweak system spec a bit more with full system image

parent b34ddb29
...@@ -36,6 +36,7 @@ class System: ...@@ -36,6 +36,7 @@ class System:
class Component(abc.ABC): class Component(abc.ABC):
def __init__(self, s: System) -> None: def __init__(self, s: System) -> None:
s.system = s s.system = s
s.parameters = {}
s.add_component(self) s.add_component(self)
@abc.abstractmethod @abc.abstractmethod
......
...@@ -34,17 +34,24 @@ class Application(abc.ABC): ...@@ -34,17 +34,24 @@ class Application(abc.ABC):
# Note AK: Maybe we can factor most of the duplicate calls with the host out # Note AK: Maybe we can factor most of the duplicate calls with the host out
# into a separate module. # into a separate module.
class LinuxApplication(abc.ABC): class BaseLinuxApplication(abc.ABC):
def __init__(self, h: base.LinuxHost) -> None: def __init__(self, h: base.LinuxHost) -> None:
self.host = h self.host = h
self.start_delay: float | None = None
self.end_delay: float | None = None
self.wait = True
@abc.abstractmethod
def run_cmds(self, env: expenv.ExpEnv) -> list[str]: def run_cmds(self, env: expenv.ExpEnv) -> list[str]:
"""Commands to run on node.""" """Commands to run on node."""
return self.app.run_cmds(self) pass
def cleanup_cmds(self, env: expenv.ExpEnv) -> list[str]: def cleanup_cmds(self, env: expenv.ExpEnv) -> list[str]:
"""Commands to run to cleanup node.""" """Commands to run to cleanup node."""
if self.end_delay is None:
return [] return []
else:
return [f'sleep {self.start_delay}']
def config_files(self, env: expenv.ExpEnv) -> dict[str, tp.IO]: def config_files(self, env: expenv.ExpEnv) -> dict[str, tp.IO]:
""" """
...@@ -68,7 +75,10 @@ class LinuxApplication(abc.ABC): ...@@ -68,7 +75,10 @@ class LinuxApplication(abc.ABC):
def prepare_post_cp(self, env: expenv.ExpEnv) -> list[str]: def prepare_post_cp(self, env: expenv.ExpEnv) -> list[str]:
"""Commands to run to prepare node after checkpoint restore.""" """Commands to run to prepare node after checkpoint restore."""
if self.end_delay is None:
return [] return []
else:
return [f'sleep {self.end_delay}']
def strfile(self, s: str) -> io.BytesIO: def strfile(self, s: str) -> io.BytesIO:
""" """
...@@ -79,3 +89,48 @@ class LinuxApplication(abc.ABC): ...@@ -79,3 +89,48 @@ class LinuxApplication(abc.ABC):
simulated node. simulated node.
""" """
return io.BytesIO(bytes(s, encoding="UTF-8")) return io.BytesIO(bytes(s, encoding="UTF-8"))
class PingClient(BaseLinuxApplication):
def __init__(self, server_ip: str = '192.168.64.1') -> None:
super().__init__()
self.server_ip = server_ip
def run_cmds(self, env: expenv.ExpEnv) -> tp.List[str]:
return [f'ping {self.server_ip} -c 10']
class Sleep(BaseLinuxApplication):
def __init__(self, delay: float = 10) -> None:
super().__init__()
self.delay = delay
def run_cmds(self, env: expenv.ExpEnv) -> tp.List[str]:
return [f'sleep {self.delay}']
class NetperfServer(BaseLinuxApplication):
def __init__(self) -> None:
super().__init__()
def run_cmds(self, env: expenv.ExpEnv) -> tp.List[str]:
return ['netserver', 'sleep infinity']
class NetperfClient(BaseLinuxApplication):
def __init__(self, server_ip: str = '192.168.64.1') -> None:
super().__init__()
self.server_ip = server_ip
self.duration_tp = 10
self.duration_lat = 10
def run_cmds(self, env: expenv.ExpEnv) -> tp.List[str]:
return [
'netserver',
'sleep 0.5',
f'netperf -H {self.server_ip} -l {self.duration_tp}',
(
f'netperf -H {self.server_ip} -l {self.duration_lat} -t TCP_RR'
' -- -o mean_latency,p50_latency,p90_latency,p99_latency'
)
]
\ No newline at end of file
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
import typing as tp import typing as tp
import io import io
from os import path from os import path
from simbricks.orchestration.experiment import experiment_environment as expenv
from simbricks.orchestration.system import base from simbricks.orchestration.system import base
from simbricks.orchestration.system import eth from simbricks.orchestration.system import eth
from simbricks.orchestration.system import mem from simbricks.orchestration.system import mem
...@@ -34,8 +35,8 @@ from simbricks.orchestration.system.host import app ...@@ -34,8 +35,8 @@ from simbricks.orchestration.system.host import app
class Host(base.Component): class Host(base.Component):
def __init__(self, s: base.System): def __init__(self, s: base.System):
super().__init__(s) super().__init__(s)
self.ifs: list[pcie.PCIeHostInterface] = [] self.ifs: list[base.Interface] = []
self.applications: list[Application] self.applications: list[app.Application]
def interfaces(self) -> list[base.Interface]: def interfaces(self) -> list[base.Interface]:
return self.pcie_ifs + self.eth_ifs + self.mem_ifs return self.pcie_ifs + self.eth_ifs + self.mem_ifs
...@@ -43,7 +44,7 @@ class Host(base.Component): ...@@ -43,7 +44,7 @@ class Host(base.Component):
def add_if(self, i: base.Interface) -> None: def add_if(self, i: base.Interface) -> None:
self.ifs.append(i) self.ifs.append(i)
def add_app(self, a: Application) -> None: def add_app(self, a: app.Application) -> None:
self.applications.append(a) self.applications.append(a)
...@@ -55,27 +56,42 @@ class FullSystemHost(Host): ...@@ -55,27 +56,42 @@ class FullSystemHost(Host):
self.cpu_freq = '3GHz' self.cpu_freq = '3GHz'
self.disks: list[disk_images.DiskImage] = [] self.disks: list[disk_images.DiskImage] = []
def add_disk(self, disk: DiskImage) -> None: def add_disk(self, disk: disk_images.DiskImage) -> None:
self.disks.append(disk) self.disks.append(disk)
class LinuxHost(FullSystemHost): class BaseLinuxHost(FullSystemHost):
def __init__(self, s: base.System) -> None: def __init__(self, s: base.System) -> None:
super().__init__(s) super().__init__(s)
self.applications: list[LinuxApplication] = [] self.applications: list[app.BaseLinuxApplication] = []
self.load_modules = [] self.load_modules = []
self.kcmd_append = '' self.kcmd_append = ''
def add_app(self, a: LinuxApplication) -> None: def add_app(self, a: app.BaseLinuxApplication) -> None:
self.applications.append(a) self.applications.append(a)
def _concat_app_cmds(
self,
env: expenv.ExpEnv,
mapper: tp.Callable[[app.LinuxApplication, expenv.ExpEnv],
list[str]]
) -> list[str]:
"""
Generate command list from applications by applying `mapper` to each
application on this host and concatenating the commands.
"""
cmds = []
for app in self.applications:
cmds += mapper(app, env)
return cmds
def run_cmds(self, env: expenv.ExpEnv) -> list[str]: def run_cmds(self, env: expenv.ExpEnv) -> list[str]:
"""Commands to run on node.""" """Commands to run on node."""
return self.app.run_cmds(self) return self._concat_app_cmds(env, app.LinuxApplication.run_cmds)
def cleanup_cmds(self, env: expenv.ExpEnv) -> list[str]: def cleanup_cmds(self, env: expenv.ExpEnv) -> list[str]:
"""Commands to run to cleanup node.""" """Commands to run to cleanup node."""
return [] return self._concat_app_cmds(env, app.LinuxApplication.cleanup_cmds)
def config_files(self, env: expenv.ExpEnv) -> dict[str, tp.IO]: def config_files(self, env: expenv.ExpEnv) -> dict[str, tp.IO]:
""" """
...@@ -92,17 +108,24 @@ class LinuxHost(FullSystemHost): ...@@ -92,17 +108,24 @@ class LinuxHost(FullSystemHost):
def prepare_pre_cp(self, env: expenv.ExpEnv) -> list[str]: def prepare_pre_cp(self, env: expenv.ExpEnv) -> list[str]:
"""Commands to run to prepare node before checkpointing.""" """Commands to run to prepare node before checkpointing."""
return [ self._concat_app_cmds(env, app.LinuxApplication.prepare_pre_cp)
'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, env: expenv.ExpEnv) -> list[str]: def prepare_post_cp(self, env: expenv.ExpEnv) -> list[str]:
"""Commands to run to prepare node after checkpoint restore.""" """Commands to run to prepare node after checkpoint restore."""
return [] return self._concat_app_cmds(env, app.LinuxApplication.prepare_post_cp)
def _config_str(self, env: expenv.ExpEnv) -> str:
if env.create_cp:
sim = env.exp.get_simulator(self)
cp_cmd = self.checkpoint_commands()
else:
cp_cmd = []
es = self.prepare_pre_cp(env) + self.app.prepare_pre_cp(env) + \
cp_cmd + \
self.prepare_post_cp(env) + self.app.prepare_post_cp(env) + \
self.run_cmds() + self.cleanup_cmds()
return '\n'.join(es)
def strfile(self, s: str) -> io.BytesIO: def strfile(self, s: str) -> io.BytesIO:
""" """
...@@ -113,3 +136,70 @@ class LinuxHost(FullSystemHost): ...@@ -113,3 +136,70 @@ class LinuxHost(FullSystemHost):
simulated node. simulated node.
""" """
return io.BytesIO(bytes(s, encoding='UTF-8')) return io.BytesIO(bytes(s, encoding='UTF-8'))
class LinuxHost(BaseLinuxHost):
def __init__(self, sys) -> None:
super().__init__(sys)
self.drivers: list[str] = []
def cleanup_cmds(self, env: expenv.ExpEnv) -> list[str]:
return super().cleanup_cmds(env) + ['poweroff -f']
def prepare_pre_cp(self, env: expenv.ExpEnv) -> 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"'
] + super().prepare_pre_cp(env)
def prepare_post_cp(self) -> tp.List[str]:
l = []
for d in self.drivers:
if d[0] == '/':
l.append(f'insmod {d}')
else:
l.append(f'modprobe {d}')
eth_i = 0
for i in self.interfaces():
# Get ifname parameter if set, otherwise default to ethX
if 'ifname' in i.parameters:
ifn = i.parameters['ifname']
elif isinstance(i, eth.EthSimpleNIC):
ifn = f'eth{eth_i}'
eth_i += 1
else:
continue
# Force MAC if requested
if 'force_mac_addr' in i.parameters:
l.append(f'ip link set dev {ifn} address '
f'{i.parameters['force_mac_addr']}')
# Bring interface up
l.append(f'ip link set dev {ifn} up')
# Add IP addresses if included
if 'ipv4_addrs' in i.parameters:
for a in i.parameters['ipv4_addrs']:
l.append(f'ip addr add {a} dev {ifn}')
return super().prepare_post_cp() + l
class I40ELinuxHost(LinuxHost):
def __init__(self, sys) -> None:
super().__init__(sys)
self.drivers.append('i40e')
class CorundumLinuxHost(LinuxHost):
def __init__(self, sys) -> None:
super().__init__(sys)
self.drivers.append('/tmp/guest/mqnic.ko')
def config_files(self, env: expenv.ExpEnv) -> tp.Dict[str, tp.IO]:
m = {'mqnic.ko': open('../images/mqnic/mqnic.ko', 'rb')}
return {**m, **super().config_files()}
\ No newline at end of file
...@@ -21,13 +21,15 @@ ...@@ -21,13 +21,15 @@
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import abc import abc
import io
import os.path import os.path
import tarfile
from simbricks.orchestration.system.host import base from simbricks.orchestration.system.host import base
from simbricks.orchestration.experiment import experiment_environment as expenv from simbricks.orchestration.experiment import experiment_environment as expenv
class DiskImage(abc.ABC): class DiskImage(abc.ABC):
def __init__(self, h: host.Host) -> None: def __init__(self, h: base.Host) -> None:
self.host = h self.host = h
@abc.abstractmethod @abc.abstractmethod
...@@ -41,7 +43,7 @@ class DiskImage(abc.ABC): ...@@ -41,7 +43,7 @@ class DiskImage(abc.ABC):
# Disk image where user just provides a path # Disk image where user just provides a path
class ExternalDiskImage(DiskImage): class ExternalDiskImage(DiskImage):
def __init__(self, h: host.FullSystemHost, path: str) -> None: def __init__(self, h: base.FullSystemHost, path: str) -> None:
super().__init__(h) super().__init__(h)
self.path = path self.path = path
self.formats = ["raw", "qcow2"] self.formats = ["raw", "qcow2"]
...@@ -56,7 +58,7 @@ class ExternalDiskImage(DiskImage): ...@@ -56,7 +58,7 @@ class ExternalDiskImage(DiskImage):
# Disk images shipped with simbricks # Disk images shipped with simbricks
class DistroDiskImage(DiskImage): class DistroDiskImage(DiskImage):
def __init__(self, h: host.FullSystemHost, name: str) -> None: def __init__(self, h: base.FullSystemHost, name: str) -> None:
super().__init__(h) super().__init__(h)
self.name = name self.name = name
self.formats = ["raw", "qcow2"] self.formats = ["raw", "qcow2"]
...@@ -78,22 +80,44 @@ class DistroDiskImage(DiskImage): ...@@ -78,22 +80,44 @@ class DistroDiskImage(DiskImage):
# Builds the Tar with the commands to run etc. # Builds the Tar with the commands to run etc.
class LinuxConfigDiskImage(DiskImage): class LinuxConfigDiskImage(DiskImage):
def __init__(self, h: host.LinuxHost) -> None: def __init__(self, h: base.LinuxHost) -> None:
super().__init__(h) super().__init__(h)
self.host: base.LinuxHost
def available_formats(self) -> list[str]: def available_formats(self) -> list[str]:
return ["raw"] return ["raw"]
async def prepare_image_path(self, env: expenv.ExpEnv, format: str) -> str: async def prepare_image_path(self, env: expenv.ExpEnv, format: str) -> str:
# TODO: build tar from host path parameters and then return path # TODO: build tar from host path parameters and then return path
pass path = env.dynamic_img_path(self, format)
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.host.strfile(self.host.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.host.config_files(env).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()
# This is an additional example: building disk images directly from python # This is an additional example: building disk images directly from python
# Could of course also have a version that generates the packer config from # Could of course also have a version that generates the packer config from
# python # python
class PackerDiskImage(DiskImage): class PackerDiskImage(DiskImage):
def __init__(self, h: host.FullSystemHost, packer_config_path: str) -> None: def __init__(self, h: base.FullSystemHost, packer_config_path: str) -> None:
super().__init__(h) super().__init__(h)
self.config_path = packer_config_path self.config_path = packer_config_path
......
Subproject commit 2c500a6a7527a1305e1a8e03f53ea11e90b71b73 Subproject commit 8e4978a2ece997efa1fccb33e945c0c496587f34
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