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

added toJSON methods for system module

parent 789df39b
...@@ -277,7 +277,7 @@ class Simulation(utils_base.IdObj): ...@@ -277,7 +277,7 @@ class Simulation(utils_base.IdObj):
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) -> None: def __init__(self, name: str, system: sys_conf.System) -> None:
super().__init__() super().__init__()
self.name = name self.name = name
""" """
...@@ -285,6 +285,7 @@ class Simulation(utils_base.IdObj): ...@@ -285,6 +285,7 @@ class Simulation(utils_base.IdObj):
Can be used to run only a selection of experiments. Can be used to run only a selection of experiments.
""" """
self.system: sys_conf.System = system
self.timeout: int | None = None self.timeout: int | None = None
"""Timeout for experiment in seconds.""" """Timeout for experiment in seconds."""
self.metadata: dict[str, tp.Any] = {} self.metadata: dict[str, tp.Any] = {}
......
...@@ -29,17 +29,36 @@ from simbricks.orchestration.utils import base as util_base ...@@ -29,17 +29,36 @@ from simbricks.orchestration.utils import base as util_base
if tp.TYPE_CHECKING: if tp.TYPE_CHECKING:
from simbricks.orchestration.instantiation import base as inst_base from simbricks.orchestration.instantiation import base as inst_base
class System(util_base.IdObj):
class System:
"""Defines System configuration of the whole simulation""" """Defines System configuration of the whole simulation"""
def __init__(self) -> None: def __init__(self) -> None:
super().__init__()
self.all_component: list[Component] = [] self.all_component: list[Component] = []
def add_component(self, c: Component) -> None: def add_component(self, c: Component) -> None:
assert c.system == self assert c.system == self
self.all_component.append(c) self.all_component.append(c)
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["type"] = self.__class__.__name__
json_obj["module"] = self.__class__.__module__
components_json = []
for comp in self.all_component:
util_base.has_attribute(comp, 'toJSON')
comp_json = comp.toJSON()
components_json.append(comp_json)
json_obj["all_component"] = components_json
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class Component(util_base.IdObj): class Component(util_base.IdObj):
...@@ -64,6 +83,26 @@ class Component(util_base.IdObj): ...@@ -64,6 +83,26 @@ class Component(util_base.IdObj):
async def prepare(self, inst: inst_base.Instantiation) -> None: async def prepare(self, inst: inst_base.Instantiation) -> None:
pass pass
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["type"] = self.__class__.__name__
json_obj["module"] = self.__class__.__module__
json_obj["system"] = self.system.id()
json_obj["name"] = self.name
interfaces_json = []
for inf in self.interfaces():
util_base.has_attribute(inf, 'toJSON')
interfaces_json.append(inf.toJSON())
json_obj["interfaces"] = interfaces_json
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class Interface(util_base.IdObj): class Interface(util_base.IdObj):
def __init__(self, c: Component) -> None: def __init__(self, c: Component) -> None:
...@@ -104,6 +143,19 @@ class Interface(util_base.IdObj): ...@@ -104,6 +143,19 @@ class Interface(util_base.IdObj):
def filter_by_type(interfaces: list[Interface], ty: type[T]) -> list[T]: def filter_by_type(interfaces: list[Interface], ty: type[T]) -> list[T]:
return list(filter(lambda inf: isinstance(inf, ty), interfaces)) return list(filter(lambda inf: isinstance(inf, ty), interfaces))
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["type"] = self.__class__.__name__
json_obj["module"] = self.__class__.__module__
json_obj["component"] = self.component.id()
json_obj["channel"] = self.channel.id()
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class Channel(util_base.IdObj): class Channel(util_base.IdObj):
def __init__(self, a: Interface, b: Interface) -> None: def __init__(self, a: Interface, b: Interface) -> None:
...@@ -131,3 +183,17 @@ class Channel(util_base.IdObj): ...@@ -131,3 +183,17 @@ class Channel(util_base.IdObj):
opposing = self.a if interface is self.b else self.b opposing = self.a if interface is self.b else self.b
assert opposing != interface assert opposing != interface
return opposing return opposing
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["type"] = self.__class__.__name__
json_obj["module"] = self.__class__.__module__
json_obj["latency"] = self.latency
json_obj["interface_a"] = self.a.id()
json_obj["interface_b"] = self.b.id()
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
...@@ -52,6 +52,17 @@ class EthSimpleNIC(base.Component): ...@@ -52,6 +52,17 @@ class EthSimpleNIC(base.Component):
def add_if(self, interface: EthInterface) -> None: def add_if(self, interface: EthInterface) -> None:
raise Exception("EthSimpleNIC already has ethernet interface") raise Exception("EthSimpleNIC already has ethernet interface")
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["ip"] = self._ip
json_obj["eth_if"] = self._eth_if.id()
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class BaseEthNetComponent(base.Component): class BaseEthNetComponent(base.Component):
def __init__(self, s: base.System) -> None: def __init__(self, s: base.System) -> None:
super().__init__(s) super().__init__(s)
...@@ -63,6 +74,17 @@ class BaseEthNetComponent(base.Component): ...@@ -63,6 +74,17 @@ class BaseEthNetComponent(base.Component):
def interfaces(self) -> list[EthInterface]: def interfaces(self) -> list[EthInterface]:
return self.eth_ifs return self.eth_ifs
def toJSON(self) -> dict:
json_obj = super().toJSON()
eth_interfaces = [inf.id() for inf in self.eth_ifs]
json_obj["eth_if"] = eth_interfaces
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class EthWire(BaseEthNetComponent): class EthWire(BaseEthNetComponent):
def __init__(self, s: base.System) -> None: def __init__(self, s: base.System) -> None:
......
...@@ -26,21 +26,35 @@ import typing as tp ...@@ -26,21 +26,35 @@ import typing as tp
import abc import abc
import io import io
from simbricks.orchestration.instantiation import base as inst_base from simbricks.orchestration.instantiation import base as inst_base
from simbricks.orchestration.utils import base as utils_base
if tp.TYPE_CHECKING: if tp.TYPE_CHECKING:
from simbricks.orchestration.system import host as sys_host from simbricks.orchestration.system import host as sys_host
class Application(abc.ABC): class Application(utils_base.IdObj):
def __init__(self, h: sys_host.Host) -> None: def __init__(self, h: sys_host.Host) -> None:
super().__init__()
self.host = h self.host = h
def toJSON(self) -> dict:
json_obj = {}
json_obj["type"] = self.__class__.__name__
json_obj["module"] = self.__class__.__module__
json_obj["host"] = self.host.id()
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
# 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 BaseLinuxApplication(abc.ABC): class BaseLinuxApplication(Application):
def __init__(self, h: sys_host.LinuxHost) -> None: def __init__(self, h: sys_host.LinuxHost) -> None:
self.host = h super().__init__(h)
self.start_delay: float | None = None self.start_delay: float | None = None
self.end_delay: float | None = None self.end_delay: float | None = None
self.wait = False self.wait = False
...@@ -88,6 +102,18 @@ class BaseLinuxApplication(abc.ABC): ...@@ -88,6 +102,18 @@ class BaseLinuxApplication(abc.ABC):
""" """
return io.BytesIO(bytes(s, encoding="UTF-8")) return io.BytesIO(bytes(s, encoding="UTF-8"))
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["start_delay"] = self.start_delay
json_obj["end_delay"] = self.end_delay
json_obj["wait"] = self.wait
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class PingClient(BaseLinuxApplication): class PingClient(BaseLinuxApplication):
def __init__(self, h: sys_host.LinuxHost, server_ip: str = "192.168.64.1") -> None: def __init__(self, h: sys_host.LinuxHost, server_ip: str = "192.168.64.1") -> None:
...@@ -97,6 +123,16 @@ class PingClient(BaseLinuxApplication): ...@@ -97,6 +123,16 @@ class PingClient(BaseLinuxApplication):
def run_cmds(self, inst: inst_base.Instantiation) -> tp.List[str]: def run_cmds(self, inst: inst_base.Instantiation) -> tp.List[str]:
return [f"ping {self.server_ip} -c 10"] return [f"ping {self.server_ip} -c 10"]
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["server_ip"] = self.server_ip
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class Sleep(BaseLinuxApplication): class Sleep(BaseLinuxApplication):
def __init__(self, h: sys_host.LinuxHost, delay: float = 10, infinite: bool = False) -> None: def __init__(self, h: sys_host.LinuxHost, delay: float = 10, infinite: bool = False) -> None:
...@@ -109,6 +145,17 @@ class Sleep(BaseLinuxApplication): ...@@ -109,6 +145,17 @@ class Sleep(BaseLinuxApplication):
return [f"sleep infinity"] return [f"sleep infinity"]
return [f"sleep {self.delay}"] return [f"sleep {self.delay}"]
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["infinite"] = self.infinite
json_obj["delay"] = self.delay
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class NetperfServer(BaseLinuxApplication): class NetperfServer(BaseLinuxApplication):
def __init__(self, h: sys_host.LinuxHost) -> None: def __init__(self, h: sys_host.LinuxHost) -> None:
...@@ -121,9 +168,9 @@ class NetperfServer(BaseLinuxApplication): ...@@ -121,9 +168,9 @@ class NetperfServer(BaseLinuxApplication):
class NetperfClient(BaseLinuxApplication): class NetperfClient(BaseLinuxApplication):
def __init__(self, h: sys_host.LinuxHost, server_ip: str = "192.168.64.1") -> None: def __init__(self, h: sys_host.LinuxHost, server_ip: str = "192.168.64.1") -> None:
super().__init__(h) super().__init__(h)
self.server_ip = server_ip self.server_ip: str = server_ip
self.duration_tp = 10 self.duration_tp: int = 10
self.duration_lat = 10 self.duration_lat: int = 10
def run_cmds(self, inst: inst_base.Instantiation) -> list[str]: def run_cmds(self, inst: inst_base.Instantiation) -> list[str]:
return [ return [
...@@ -135,3 +182,15 @@ class NetperfClient(BaseLinuxApplication): ...@@ -135,3 +182,15 @@ class NetperfClient(BaseLinuxApplication):
" -- -o mean_latency,p50_latency,p90_latency,p99_latency" " -- -o mean_latency,p50_latency,p90_latency,p99_latency"
), ),
] ]
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["server_ip"] = self.server_ip
json_obj["duration_tp"] = self.duration_tp
json_obj["duration_lat"] = self.duration_lat
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
...@@ -40,7 +40,7 @@ class Host(base.Component): ...@@ -40,7 +40,7 @@ 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[base.Interface] = [] self.ifs: list[base.Interface] = []
self.applications: list[app.Application] self.applications: list[app.Application] = []
def interfaces(self) -> list[base.Interface]: def interfaces(self) -> list[base.Interface]:
return self.ifs return self.ifs
...@@ -51,6 +51,22 @@ class Host(base.Component): ...@@ -51,6 +51,22 @@ class Host(base.Component):
def add_app(self, a: app.Application) -> None: def add_app(self, a: app.Application) -> None:
self.applications.append(a) self.applications.append(a)
def toJSON(self) -> dict:
json_obj = super().toJSON()
applications_json = []
for app in self.applications:
utils_base.has_attribute(app, 'toJSON')
applications_json.append(app.toJSON())
json_obj["applications"] = applications_json
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class FullSystemHost(Host): class FullSystemHost(Host):
def __init__(self, s: base.System) -> None: def __init__(self, s: base.System) -> None:
...@@ -67,6 +83,25 @@ class FullSystemHost(Host): ...@@ -67,6 +83,25 @@ class FullSystemHost(Host):
promises = [disk.prepare(inst) for disk in self.disks] promises = [disk.prepare(inst) for disk in self.disks]
await asyncio.gather(*promises) await asyncio.gather(*promises)
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["memory"] = self.memory
json_obj["cores"] = self.cores
json_obj["cpu_freq"] = self.cpu_freq
disks_json = []
for disk in self.disks:
utils_base.has_attribute(disk, 'toJSON')
disks_json.append(disk.toJSON())
json_obj["disks"] = disks_json
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class BaseLinuxHost(FullSystemHost): class BaseLinuxHost(FullSystemHost):
def __init__(self, s: base.System) -> None: def __init__(self, s: base.System) -> None:
...@@ -166,6 +201,17 @@ class BaseLinuxHost(FullSystemHost): ...@@ -166,6 +201,17 @@ class BaseLinuxHost(FullSystemHost):
""" """
return io.BytesIO(bytes(s, encoding="UTF-8")) return io.BytesIO(bytes(s, encoding="UTF-8"))
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["load_modules"] = self.load_modules
json_obj["kcmd_append"] = self.kcmd_append
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class LinuxHost(BaseLinuxHost): class LinuxHost(BaseLinuxHost):
def __init__(self, sys) -> None: def __init__(self, sys) -> None:
...@@ -230,6 +276,17 @@ class LinuxHost(BaseLinuxHost): ...@@ -230,6 +276,17 @@ class LinuxHost(BaseLinuxHost):
return cmds return cmds
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["drivers"] = self.drivers
json_obj["hostname"] = self.hostname
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class I40ELinuxHost(LinuxHost): class I40ELinuxHost(LinuxHost):
def __init__(self, sys) -> None: def __init__(self, sys) -> None:
......
...@@ -84,6 +84,19 @@ class DiskImage(utils_base.IdObj): ...@@ -84,6 +84,19 @@ class DiskImage(utils_base.IdObj):
await self._prepare_format(inst, format) await self._prepare_format(inst, format)
def toJSON(self) -> dict:
json_obj = {}
json_obj["type"] = self.__class__.__name__
json_obj["module"] = self.__class__.__module__
json_obj["host"] = self.host.id()
json_obj["qemu_img_exec"] = self._qemu_img_exec
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
# Disk image where user just provides a path # Disk image where user just provides a path
class ExternalDiskImage(DiskImage): class ExternalDiskImage(DiskImage):
...@@ -99,6 +112,17 @@ class ExternalDiskImage(DiskImage): ...@@ -99,6 +112,17 @@ class ExternalDiskImage(DiskImage):
DiskImage.assert_is_file(self._path) DiskImage.assert_is_file(self._path)
return self._path return self._path
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["path"] = self._path
json_obj["formats"] = self.formats
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
# Disk images shipped with simbricks # Disk images shipped with simbricks
class DistroDiskImage(DiskImage): class DistroDiskImage(DiskImage):
...@@ -121,6 +145,17 @@ class DistroDiskImage(DiskImage): ...@@ -121,6 +145,17 @@ class DistroDiskImage(DiskImage):
DiskImage.assert_is_file(path) DiskImage.assert_is_file(path)
return path return path
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["name"] = self.name
json_obj["formats"] = self.formats
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
# Abstract base class for dynamically generated images # Abstract base class for dynamically generated images
class DynamicDiskImage(DiskImage): class DynamicDiskImage(DiskImage):
...@@ -186,3 +221,13 @@ class PackerDiskImage(DynamicDiskImage): ...@@ -186,3 +221,13 @@ class PackerDiskImage(DynamicDiskImage):
async def _prepare_format(self, inst: inst_base.Instantiation, format: str) -> None: async def _prepare_format(self, inst: inst_base.Instantiation, format: str) -> None:
# TODO: invoke packer to build the image if necessary # TODO: invoke packer to build the image if necessary
pass pass
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["config_path"] = self.config_path
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
...@@ -68,3 +68,16 @@ class MemSimpleDevice(base.Component): ...@@ -68,3 +68,16 @@ class MemSimpleDevice(base.Component):
utils_base.has_expected_type(obj=interface, expected_type=MemDeviceInterface) utils_base.has_expected_type(obj=interface, expected_type=MemDeviceInterface)
assert self._mem_if is None assert self._mem_if is None
self._mem_if = interface self._mem_if = interface
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["mem_if"] = self._mem_if.id()
json_obj["addr"] = self._addr
json_obj["size"] = self._size
json_obj["as_id"] = self._as_id
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
\ No newline at end of file
...@@ -63,3 +63,13 @@ class PCIeSimpleDevice(base.Component): ...@@ -63,3 +63,13 @@ class PCIeSimpleDevice(base.Component):
def add_if(self, interface: PCIeDeviceInterface) -> None: def add_if(self, interface: PCIeDeviceInterface) -> None:
raise Exception("PCIeSimpleDevice already has PCI device interface") raise Exception("PCIeSimpleDevice already has PCI device interface")
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["pci_if"] = self._pci_if.id()
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
\ No newline at end of file
...@@ -30,6 +30,18 @@ class IdObj(abc.ABC): ...@@ -30,6 +30,18 @@ class IdObj(abc.ABC):
def __init__(self): def __init__(self):
self._id = next(self.__id_iter) self._id = next(self.__id_iter)
def id(self) -> int:
return self._id
def toJSON(self):
json_obj = {}
json_obj["id"] = self._id
return json_obj
@staticmethod
def fromJSON(json_obj):
# TODO
pass
def check_type(obj, expected_type) -> bool: def check_type(obj, expected_type) -> bool:
""" """
...@@ -45,3 +57,9 @@ def has_expected_type(obj, expected_type) -> None: ...@@ -45,3 +57,9 @@ def has_expected_type(obj, expected_type) -> None:
raise Exception( raise Exception(
f"obj of type {type(obj)} has not the type or is not a subtype of {expected_type}" f"obj of type {type(obj)} has not the type or is not a subtype of {expected_type}"
) )
def has_attribute(obj, attr: str) -> None:
if not hasattr(obj, attr):
raise Exception(
f"obj of type {type(obj)} no attribute called {attr}"
)
# Copyright 2024 Max Planck Institute for Software Systems, and
# National University of Singapore
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import json
from simbricks.orchestration.utils import base as util_base
from simbricks.orchestration.system import base as sys_base
def toJSON(system: sys_base.System) -> dict:
json_obj = {}
util_base.has_attribute(system, "toJSON")
json_obj["system"] = system.toJSON()
channels: set[sys_base.Channel] = set()
for comp in system.all_component:
for inf in comp.interfaces():
channels.add(inf.channel)
channels_json = []
for chan in channels:
util_base.has_attribute(chan, "toJSON")
channels_json.append(chan.toJSON())
json_obj["channels"] = channels_json
return json.dumps({"specification": json_obj})
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