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

added json deserialization to system module

parent 7fac851b
...@@ -44,6 +44,8 @@ class Channel(utils_base.IdObj): ...@@ -44,6 +44,8 @@ class Channel(utils_base.IdObj):
def toJSON(self): def toJSON(self):
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["type"] = self.__class__.__name__
json_obj["module"] = self.__class__.__module__
json_obj["synchronized"] = self._synchronized json_obj["synchronized"] = self._synchronized
json_obj["sync_period"] = self.sync_period json_obj["sync_period"] = self.sync_period
json_obj["sys_channel"] = self.sys_channel.id() json_obj["sys_channel"] = self.sys_channel.id()
......
...@@ -35,11 +35,36 @@ class System(util_base.IdObj): ...@@ -35,11 +35,36 @@ class System(util_base.IdObj):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self.all_component: list[Component] = [] self._all_components: dict[int, Component] = {}
self._all_interfaces: dict[int, Interface] = {}
self._all_channels: dict[int, Channel] = {}
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) assert c.id() not in self._all_components
self._all_components[c.id()] = c
def get_comp(self, ident: int) -> Component:
assert ident in self._all_components
return self._all_components[ident]
def _add_interface(self, i: Interface) -> None:
assert i.component.id() in self._all_components
assert i.id() not in self._all_interfaces
self._all_interfaces[i.id()] = i
def get_inf(self, ident: int) -> Interface:
assert ident in self._all_interfaces
return self._all_interfaces[ident]
def _add_channel(self, c: Channel) -> None:
assert c.a.id() in self._all_interfaces and c.b.id() in self._all_interfaces
assert c.id() not in self._all_channels
self._all_channels[c.id()] = c
def get_chan(self, ident: int) -> Channel:
assert ident in self._all_channels
return self._all_channels[ident]
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
...@@ -47,19 +72,15 @@ class System(util_base.IdObj): ...@@ -47,19 +72,15 @@ class System(util_base.IdObj):
json_obj["module"] = self.__class__.__module__ json_obj["module"] = self.__class__.__module__
components_json = [] components_json = []
channels: set[Channel] = set() for _, comp in self._all_components.items():
for comp in self.all_component:
util_base.has_attribute(comp, "toJSON") util_base.has_attribute(comp, "toJSON")
comp_json = comp.toJSON() comp_json = comp.toJSON()
components_json.append(comp_json) components_json.append(comp_json)
for inf in comp.interfaces(): json_obj["all_components"] = components_json
channels.add(inf.channel)
json_obj["all_component"] = components_json
channels_json = [] channels_json = []
for chan in channels: for _, chan in self._all_channels.items():
util_base.has_attribute(chan, "toJSON") util_base.has_attribute(chan, "toJSON")
channels_json.append(chan.toJSON()) channels_json.append(chan.toJSON())
...@@ -67,10 +88,26 @@ class System(util_base.IdObj): ...@@ -67,10 +88,26 @@ class System(util_base.IdObj):
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, json_obj: dict) -> System:
# TODO instance = super().fromJSON(json_obj)
pass instance._all_components = {}
instance._all_interfaces = {}
instance._all_channels = {}
components_json = util_base.get_json_attr_top(json_obj, "all_components")
for comp_json in components_json:
comp_class = util_base.get_cls_by_json(comp_json)
util_base.has_attribute(comp_class, "fromJSON")
comp = comp_class.fromJSON(instance, comp_json)
channels_json = util_base.get_json_attr_top(json_obj, "channels")
for chan_json in channels_json:
chan_class = util_base.get_cls_by_json(chan_json)
util_base.has_attribute(chan_class, "fromJSON")
chan = chan_class.fromJSON(instance, chan_json)
return instance
class Component(util_base.IdObj): class Component(util_base.IdObj):
...@@ -78,17 +115,17 @@ class Component(util_base.IdObj): ...@@ -78,17 +115,17 @@ class Component(util_base.IdObj):
def __init__(self, s: System) -> None: def __init__(self, s: System) -> None:
super().__init__() super().__init__()
self.system = s self.system = s
self.ifs: list[Interface] = []
s.parameters = {} s.parameters = {}
s.add_component(self) s._add_component(self)
self.name: str = "" self.name: str | None = None
@abc.abstractmethod
def interfaces(self) -> list[Interface]: def interfaces(self) -> list[Interface]:
return [] return self.ifs
@abc.abstractmethod # NOTE: overwrite and call in subclasses
def add_if(self, interface: tp.Any) -> None: def add_if(self, interface: Interface) -> None:
raise Exception("must be overwritten by subclass") self.ifs.append(interface)
def channels(self) -> list[Channel]: def channels(self) -> list[Channel]:
return [i.channel for i in self.interfaces() if i.is_connected()] return [i.channel for i in self.interfaces() if i.is_connected()]
...@@ -111,16 +148,29 @@ class Component(util_base.IdObj): ...@@ -111,16 +148,29 @@ class Component(util_base.IdObj):
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: System, json_obj: dict) -> Component:
# TODO instance = super().fromJSON(json_obj)
pass instance.name = util_base.get_json_attr_top_or_none(json_obj, "name")
instance.system = system
system._add_component(instance)
instance.ifs = []
interfaces_json = util_base.get_json_attr_top(json_obj, "interfaces")
for inf_json in interfaces_json:
inf_class = util_base.get_cls_by_json(inf_json)
util_base.has_attribute(inf_class, "fromJSON")
# NOTE: this will add the interface to the system map for retrieval in sub-classes
inf = inf_class.fromJSON(system, inf_json)
return instance
class Interface(util_base.IdObj): class Interface(util_base.IdObj):
def __init__(self, c: Component) -> None: def __init__(self, c: Component) -> None:
super().__init__() super().__init__()
self.component = c self.component = c
c.system._add_interface(self)
self.channel: Channel | None = None self.channel: Channel | None = None
def is_connected(self) -> bool: def is_connected(self) -> bool:
...@@ -130,7 +180,10 @@ class Interface(util_base.IdObj): ...@@ -130,7 +180,10 @@ class Interface(util_base.IdObj):
self.channel = None self.channel = None
def connect(self, c: Channel) -> None: def connect(self, c: Channel) -> None:
assert self.channel is None if self.channel is not None:
raise Exception(
f"cannot connect interface {self.id()} to channel {c.id()}. interface is already connected to channel {self.channel.id()}"
)
self.channel = c self.channel = c
def find_peer(self) -> Interface: def find_peer(self) -> Interface:
...@@ -164,10 +217,16 @@ class Interface(util_base.IdObj): ...@@ -164,10 +217,16 @@ class Interface(util_base.IdObj):
json_obj["channel"] = self.channel.id() json_obj["channel"] = self.channel.id()
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: System, json_obj: dict) -> Interface:
# TODO instance = super().fromJSON(json_obj)
pass comp_id = util_base.get_json_attr_top(json_obj, "component")
comp = system.get_comp(comp_id)
instance.component = comp
comp.add_if(instance)
system._add_interface(instance)
instance.channel = None
return instance
class Channel(util_base.IdObj): class Channel(util_base.IdObj):
...@@ -178,6 +237,7 @@ class Channel(util_base.IdObj): ...@@ -178,6 +237,7 @@ class Channel(util_base.IdObj):
self.a.connect(self) self.a.connect(self)
self.b: Interface = b self.b: Interface = b
self.b.connect(self) self.b.connect(self)
a.component.system._add_channel(self)
def interfaces(self) -> list[Interface]: def interfaces(self) -> list[Interface]:
return [self.a, self.b] return [self.a, self.b]
...@@ -206,7 +266,20 @@ class Channel(util_base.IdObj): ...@@ -206,7 +266,20 @@ class Channel(util_base.IdObj):
json_obj["interface_b"] = self.b.id() json_obj["interface_b"] = self.b.id()
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: System, json_obj: dict) -> Channel:
# TODO instance = super().fromJSON(json_obj)
pass instance.latency = int(util_base.get_json_attr_top(json_obj, "latency"))
inf_id_a = int(util_base.get_json_attr_top(json_obj, "interface_a"))
inf_id_b = int(util_base.get_json_attr_top(json_obj, "interface_b"))
inf_a = system.get_inf(ident=inf_id_a)
inf_b = system.get_inf(ident=inf_id_b)
instance.a = inf_a
instance.a.connect(instance)
instance.b = inf_b
instance.b.connect(instance)
system._add_channel(instance)
return instance
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
# 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.
from __future__ import annotations
from simbricks.orchestration.system import base from simbricks.orchestration.system import base
from simbricks.orchestration.utils import base as utils_base from simbricks.orchestration.utils import base as utils_base
...@@ -44,46 +46,50 @@ class EthSimpleNIC(base.Component): ...@@ -44,46 +46,50 @@ class EthSimpleNIC(base.Component):
super().__init__(s) super().__init__(s)
self._ip: str | None = None self._ip: str | None = None
self._eth_if: EthInterface = EthInterface(self) self._eth_if: EthInterface = EthInterface(self)
super().add_if(self._eth_if)
def add_ipv4(self, ip: str) -> None: def add_ipv4(self, ip: str) -> None:
assert self._ip is None assert self._ip is None
self._ip = ip self._ip = ip
def add_if(self, interface: EthInterface) -> None: def add_if(self, interface: EthInterface) -> None:
raise Exception("EthSimpleNIC already has ethernet interface") utils_base.has_expected_type(interface, EthInterface)
if hasattr(self, "_eth_if") and self._eth_if:
raise Exception(
f"you overwrite EthSimpleNIC._eth_if ({self._eth_if.id()} -> {interface.id()}) "
)
self._eth_if = interface
super().add_if(self._eth_if)
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["ip"] = self._ip json_obj["ip"] = self._ip
json_obj["eth_if"] = self._eth_if.id() json_obj["eth_if"] = self._eth_if.id()
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: base.System, json_obj: dict) -> EthSimpleNIC:
# TODO instance = super().fromJSON(system, json_obj)
pass instance._ip = utils_base.get_json_attr_top(json_obj, "ip")
eth_inf_id = int(utils_base.get_json_attr_top(json_obj, "eth_if"))
instance._eth_if = system.get_inf(eth_inf_id)
return instance
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)
self.eth_ifs: list[EthInterface] = []
def add_if(self, i: EthInterface) -> None: def add_if(self, i: EthInterface) -> None:
self.eth_ifs.append(i) utils_base.has_expected_type(i, EthInterface)
super().add_if(i)
def interfaces(self) -> list[EthInterface]:
return self.eth_ifs
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() return super().toJSON()
eth_interfaces = [inf.id() for inf in self.eth_ifs]
json_obj["eth_if"] = eth_interfaces @classmethod
return json_obj def fromJSON(cls, system: base.System, json_obj: dict) -> BaseEthNetComponent:
return super().fromJSON(system, json_obj)
@staticmethod
def fromJSON(json_obj):
# TODO
pass
class EthWire(BaseEthNetComponent): class EthWire(BaseEthNetComponent):
...@@ -91,9 +97,9 @@ class EthWire(BaseEthNetComponent): ...@@ -91,9 +97,9 @@ class EthWire(BaseEthNetComponent):
super().__init__(s) super().__init__(s)
def add_if(self, i: EthInterface) -> None: def add_if(self, i: EthInterface) -> None:
if len(self.eth_ifs) > 2: if len(self.ifs) > 2:
raise Exception("one can only add 2 interfaces to a EthWire") raise Exception("one can only add 2 interfaces to a EthWire")
self.eth_ifs.append(i) super().add_if(i)
class EthSwitch(BaseEthNetComponent): class EthSwitch(BaseEthNetComponent):
......
...@@ -27,6 +27,7 @@ import abc ...@@ -27,6 +27,7 @@ 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 from simbricks.orchestration.utils import base as utils_base
from simbricks.orchestration.system import base as sys_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
...@@ -35,19 +36,21 @@ if tp.TYPE_CHECKING: ...@@ -35,19 +36,21 @@ if tp.TYPE_CHECKING:
class Application(utils_base.IdObj): class Application(utils_base.IdObj):
def __init__(self, h: sys_host.Host) -> None: def __init__(self, h: sys_host.Host) -> None:
super().__init__() super().__init__()
self.host = h self.host: sys_host.Host = h
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = {} json_obj = super().toJSON()
json_obj["type"] = self.__class__.__name__ json_obj["type"] = self.__class__.__name__
json_obj["module"] = self.__class__.__module__ json_obj["module"] = self.__class__.__module__
json_obj["host"] = self.host.id() json_obj["host"] = self.host.id()
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: sys_base.System, json_obj: dict):
# TODO instance = super().fromJSON(json_obj)
pass host_id = utils_base.get_json_attr_top(json_obj, "host")
instance.host = system.get_comp(host_id)
return instance
# 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
...@@ -57,7 +60,7 @@ class BaseLinuxApplication(Application): ...@@ -57,7 +60,7 @@ class BaseLinuxApplication(Application):
super().__init__(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: bool = False
@abc.abstractmethod @abc.abstractmethod
def run_cmds(self, inst: inst_base.Instantiation) -> list[str]: def run_cmds(self, inst: inst_base.Instantiation) -> list[str]:
...@@ -101,60 +104,70 @@ class BaseLinuxApplication(Application): ...@@ -101,60 +104,70 @@ class BaseLinuxApplication(Application):
simulated node. simulated node.
""" """
return io.BytesIO(bytes(s, encoding="UTF-8")) return io.BytesIO(bytes(s, encoding="UTF-8"))
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["start_delay"] = self.start_delay json_obj["start_delay"] = self.start_delay
json_obj["end_delay"] = self.end_delay json_obj["end_delay"] = self.end_delay
json_obj["wait"] = self.wait json_obj["wait"] = self.wait
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: sys_base.System, json_obj: dict):
# TODO instance = super().fromJSON(system, json_obj)
pass instance.start_delay = utils_base.get_json_attr_top_or_none(
json_obj, "start_delay"
)
instance.end_delay = utils_base.get_json_attr_top_or_none(json_obj, "end_delay")
instance.wait = utils_base.get_json_attr_top(json_obj, "wait")
return instance
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:
super().__init__(h) super().__init__(h)
self.server_ip = server_ip self.server_ip: str = server_ip
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: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["server_ip"] = self.server_ip json_obj["server_ip"] = self.server_ip
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: sys_base.System, json_obj: dict):
# TODO instance = super().fromJSON(system, json_obj)
pass instance.server_ip = utils_base.get_json_attr_top(json_obj, "server_ip")
return instance
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:
super().__init__(h) super().__init__(h)
self.infinite: bool = infinite self.infinite: bool = infinite
self.delay = delay self.delay: float = delay
def run_cmds(self, inst: inst_base.Instantiation) -> list[str]: def run_cmds(self, inst: inst_base.Instantiation) -> list[str]:
if self.infinite: if self.infinite:
return [f"sleep infinity"] return [f"sleep infinity"]
return [f"sleep {self.delay}"] return [f"sleep {self.delay}"]
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["infinite"] = self.infinite json_obj["infinite"] = self.infinite
json_obj["delay"] = self.delay json_obj["delay"] = self.delay
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: sys_base.System, json_obj: dict) -> Sleep:
# TODO instance = super().fromJSON(system, json_obj)
pass instance.infinite = bool(utils_base.get_json_attr_top(json_obj, "infinite"))
instance.delay = float(utils_base.get_json_attr_top(json_obj, "delay"))
return instance
class NetperfServer(BaseLinuxApplication): class NetperfServer(BaseLinuxApplication):
...@@ -164,6 +177,10 @@ class NetperfServer(BaseLinuxApplication): ...@@ -164,6 +177,10 @@ class NetperfServer(BaseLinuxApplication):
def run_cmds(self, inst: inst_base.Instantiation) -> list[str]: def run_cmds(self, inst: inst_base.Instantiation) -> list[str]:
return ["netserver", "sleep infinity"] return ["netserver", "sleep infinity"]
@classmethod
def fromJSON(cls, system: sys_base.System, json_obj: dict) -> NetperfServer:
return super().fromJSON(system, json_obj)
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:
...@@ -182,15 +199,22 @@ class NetperfClient(BaseLinuxApplication): ...@@ -182,15 +199,22 @@ 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: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["server_ip"] = self.server_ip json_obj["server_ip"] = self.server_ip
json_obj["duration_tp"] = self.duration_tp json_obj["duration_tp"] = self.duration_tp
json_obj["duration_lat"] = self.duration_lat json_obj["duration_lat"] = self.duration_lat
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: sys_base.System, json_obj: dict) -> NetperfClient:
# TODO instance = super().fromJSON(system, json_obj)
pass instance.server_ip = utils_base.get_json_attr_top(json_obj, "server_ip")
instance.duration_tp = int(
utils_base.get_json_attr_top(json_obj, "duration_tp")
)
instance.duration_lat = int(
utils_base.get_json_attr_top(json_obj, "duration_lat")
)
return instance
...@@ -39,15 +39,8 @@ if tp.TYPE_CHECKING: ...@@ -39,15 +39,8 @@ if tp.TYPE_CHECKING:
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[base.Interface] = []
self.applications: list[app.Application] = [] self.applications: list[app.Application] = []
def interfaces(self) -> list[base.Interface]:
return self.ifs
def add_if(self, interface: base.Interface) -> None:
self.ifs.append(interface)
def add_app(self, a: app.Application) -> None: def add_app(self, a: app.Application) -> None:
self.applications.append(a) self.applications.append(a)
...@@ -56,24 +49,33 @@ class Host(base.Component): ...@@ -56,24 +49,33 @@ class Host(base.Component):
applications_json = [] applications_json = []
for app in self.applications: for app in self.applications:
utils_base.has_attribute(app, 'toJSON') utils_base.has_attribute(app, "toJSON")
applications_json.append(app.toJSON()) applications_json.append(app.toJSON())
json_obj["applications"] = applications_json json_obj["applications"] = applications_json
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: base.System, json_obj: dict) -> Host:
# TODO instance = super().fromJSON(system=system, json_obj=json_obj)
pass instance.applications = []
applications_json = utils_base.get_json_attr_top(json_obj, "applications")
for app_json in applications_json:
app_class = utils_base.get_cls_by_json(app_json)
utils_base.has_attribute(app_class, "fromJSON")
app = app_class.fromJSON(system, app_json)
instance.add_app(app)
return instance
class FullSystemHost(Host): class FullSystemHost(Host):
def __init__(self, s: base.System) -> None: def __init__(self, s: base.System) -> None:
super().__init__(s) super().__init__(s)
self.memory = 512 self.memory: int = 512
self.cores = 1 self.cores: int = 1
self.cpu_freq = "3GHz" self.cpu_freq: str = "3GHz"
self.disks: list[disk_images.DiskImage] = [] self.disks: list[disk_images.DiskImage] = []
def add_disk(self, disk: disk_images.DiskImage) -> None: def add_disk(self, disk: disk_images.DiskImage) -> None:
...@@ -91,16 +93,28 @@ class FullSystemHost(Host): ...@@ -91,16 +93,28 @@ class FullSystemHost(Host):
disks_json = [] disks_json = []
for disk in self.disks: for disk in self.disks:
utils_base.has_attribute(disk, 'toJSON') utils_base.has_attribute(disk, "toJSON")
disks_json.append(disk.toJSON()) disks_json.append(disk.toJSON())
json_obj["disks"] = disks_json json_obj["disks"] = disks_json
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: base.System, json_obj: dict) -> FullSystemHost:
# TODO instance = super().fromJSON(system, json_obj)
pass instance.memory = int(utils_base.get_json_attr_top(json_obj, "memory"))
instance.cores = int(utils_base.get_json_attr_top(json_obj, "cores"))
instance.cpu_freq = utils_base.get_json_attr_top(json_obj, "cpu_freq")
instance.disks = []
disks_json = utils_base.get_json_attr_top(json_obj, "disks")
for disk_js in disks_json:
disk_class = utils_base.get_cls_by_json(disk_js)
utils_base.has_attribute(disk_class, "fromJSON")
disk = disk_class.fromJSON(system, disk_js)
instance.add_disk(disk)
return instance
class BaseLinuxHost(FullSystemHost): class BaseLinuxHost(FullSystemHost):
...@@ -200,17 +214,19 @@ class BaseLinuxHost(FullSystemHost): ...@@ -200,17 +214,19 @@ class BaseLinuxHost(FullSystemHost):
simulated node. simulated node.
""" """
return io.BytesIO(bytes(s, encoding="UTF-8")) return io.BytesIO(bytes(s, encoding="UTF-8"))
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["load_modules"] = self.load_modules json_obj["load_modules"] = self.load_modules
json_obj["kcmd_append"] = self.kcmd_append json_obj["kcmd_append"] = self.kcmd_append
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: base.System, json_obj: dict) -> BaseLinuxHost:
# TODO instance = super().fromJSON(system, json_obj)
pass instance.load_modules = utils_base.get_json_attr_top(json_obj, "load_modules")
instance.kcmd_append = utils_base.get_json_attr_top(json_obj, "kcmd_append")
return instance
class LinuxHost(BaseLinuxHost): class LinuxHost(BaseLinuxHost):
...@@ -275,17 +291,19 @@ class LinuxHost(BaseLinuxHost): ...@@ -275,17 +291,19 @@ class LinuxHost(BaseLinuxHost):
cmds.append(f"ip addr add {com._ip}/24 dev {ifn}") cmds.append(f"ip addr add {com._ip}/24 dev {ifn}")
return cmds return cmds
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["drivers"] = self.drivers json_obj["drivers"] = self.drivers
json_obj["hostname"] = self.hostname json_obj["hostname"] = self.hostname
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: base.System, json_obj: dict) -> LinuxHost:
# TODO instance = super().fromJSON(system, json_obj)
pass instance.drivers = utils_base.get_json_attr_top(json_obj, "drivers")
instance.hostname = utils_base.get_json_attr_top(json_obj, "hostname")
return instance
class I40ELinuxHost(LinuxHost): class I40ELinuxHost(LinuxHost):
......
...@@ -29,6 +29,7 @@ import tarfile ...@@ -29,6 +29,7 @@ import tarfile
import typing as tp import typing as tp
from simbricks.orchestration.utils import base as utils_base from simbricks.orchestration.utils import base as utils_base
from simbricks.orchestration.instantiation import base as inst_base from simbricks.orchestration.instantiation import base as inst_base
from simbricks.orchestration.system import base as sys_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
...@@ -85,17 +86,22 @@ class DiskImage(utils_base.IdObj): ...@@ -85,17 +86,22 @@ class DiskImage(utils_base.IdObj):
await self._prepare_format(inst, format) await self._prepare_format(inst, format)
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = {} json_obj = super().toJSON()
json_obj["type"] = self.__class__.__name__ json_obj["type"] = self.__class__.__name__
json_obj["module"] = self.__class__.__module__ json_obj["module"] = self.__class__.__module__
json_obj["host"] = self.host.id() json_obj["host"] = self.host.id()
json_obj["qemu_img_exec"] = self._qemu_img_exec json_obj["qemu_img_exec"] = self._qemu_img_exec
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: sys_base.System, json_obj: dict) -> DiskImage:
# TODO instance = super().fromJSON(json_obj)
pass host_id = int(utils_base.get_json_attr_top(json_obj, "host"))
instance.host = system.get_comp(host_id)
instance._qemu_img_exec = utils_base.get_json_attr_top(
json_obj, "qemu_img_exec"
)
return instance
# Disk image where user just provides a path # Disk image where user just provides a path
...@@ -111,17 +117,19 @@ class ExternalDiskImage(DiskImage): ...@@ -111,17 +117,19 @@ class ExternalDiskImage(DiskImage):
def path(self, inst: inst_base.Instantiation, format: str) -> str: def path(self, inst: inst_base.Instantiation, format: str) -> str:
DiskImage.assert_is_file(self._path) DiskImage.assert_is_file(self._path)
return self._path return self._path
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["path"] = self._path json_obj["path"] = self._path
json_obj["formats"] = self.formats json_obj["formats"] = self.formats
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: sys_base.System, json_obj: dict) -> DiskImage:
# TODO instance = super().fromJSON(system, json_obj)
pass instance._path = utils_base.get_json_attr_top(json_obj, "path")
instance.formats = utils_base.get_json_attr_top(json_obj, "formats")
return instance
# Disk images shipped with simbricks # Disk images shipped with simbricks
...@@ -144,17 +152,19 @@ class DistroDiskImage(DiskImage): ...@@ -144,17 +152,19 @@ class DistroDiskImage(DiskImage):
raise RuntimeError("Unsupported disk format") raise RuntimeError("Unsupported disk format")
DiskImage.assert_is_file(path) DiskImage.assert_is_file(path)
return path return path
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["name"] = self.name json_obj["name"] = self.name
json_obj["formats"] = self.formats json_obj["formats"] = self.formats
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: sys_base.System, json_obj: dict) -> DiskImage:
# TODO instance = super().fromJSON(system, json_obj)
pass instance.name = utils_base.get_json_attr_top(json_obj, "name")
instance.formats = utils_base.get_json_attr_top(json_obj, "formats")
return instance
# Abstract base class for dynamically generated images # Abstract base class for dynamically generated images
...@@ -169,6 +179,10 @@ class DynamicDiskImage(DiskImage): ...@@ -169,6 +179,10 @@ class DynamicDiskImage(DiskImage):
async def _prepare_format(self, inst: inst_base.Instantiation, format: str) -> None: async def _prepare_format(self, inst: inst_base.Instantiation, format: str) -> None:
pass pass
@classmethod
def fromJSON(cls, system: sys_base.System, json_obj: dict) -> DynamicDiskImage:
return super().fromJSON(system, json_obj)
# Builds the Tar with the commands to run etc. # Builds the Tar with the commands to run etc.
class LinuxConfigDiskImage(DynamicDiskImage): class LinuxConfigDiskImage(DynamicDiskImage):
...@@ -206,6 +220,10 @@ class LinuxConfigDiskImage(DynamicDiskImage): ...@@ -206,6 +220,10 @@ class LinuxConfigDiskImage(DynamicDiskImage):
tar.addfile(tarinfo=f_i, fileobj=f) tar.addfile(tarinfo=f_i, fileobj=f)
f.close() f.close()
@classmethod
def fromJSON(cls, system: sys_base.System, json_obj: dict) -> LinuxConfigDiskImage:
return super().fromJSON(system, json_obj)
# 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
...@@ -226,8 +244,9 @@ class PackerDiskImage(DynamicDiskImage): ...@@ -226,8 +244,9 @@ class PackerDiskImage(DynamicDiskImage):
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["config_path"] = self.config_path json_obj["config_path"] = self.config_path
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: sys_base.System, json_obj: dict) -> PackerDiskImage:
# TODO instance = super().fromJSON(system, json_obj)
pass instance.config_path = utils_base.get_json_attr_top(json_obj, "config_path")
return instance
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
# 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.
from __future__ import annotations
from simbricks.orchestration.system import base from simbricks.orchestration.system import base
from simbricks.orchestration.utils import base as utils_base from simbricks.orchestration.utils import base as utils_base
...@@ -38,7 +40,7 @@ class MemDeviceInterface(base.Interface): ...@@ -38,7 +40,7 @@ class MemDeviceInterface(base.Interface):
def connect(self, c: base.Channel) -> None: def connect(self, c: base.Channel) -> None:
# Note AK: a bit ugly, but I think we can't get around a rt check here # Note AK: a bit ugly, but I think we can't get around a rt check here
if not c is isinstance(c, MemChannel): if not c is isinstance(c, MemChannel):
raise TypeError('MemDeviceInterface only connects to MemChannel') raise TypeError("MemDeviceInterface only connects to MemChannel")
super().connect(c) super().connect(c)
...@@ -48,7 +50,7 @@ class MemChannel(base.Channel): ...@@ -48,7 +50,7 @@ class MemChannel(base.Channel):
def host_if(self) -> MemHostInterface: def host_if(self) -> MemHostInterface:
return self.a return self.a
def dev_if(self) -> MemDeviceInterface: def dev_if(self) -> MemDeviceInterface:
return self.b return self.b
...@@ -57,17 +59,19 @@ class MemSimpleDevice(base.Component): ...@@ -57,17 +59,19 @@ class MemSimpleDevice(base.Component):
def __init__(self, s: base.System): def __init__(self, s: base.System):
super().__init__(s) super().__init__(s)
self._mem_if: MemDeviceInterface = MemDeviceInterface(c=self) self._mem_if: MemDeviceInterface = MemDeviceInterface(c=self)
self._addr = 0xe000000000000000 super().add_if(self._mem_if)
self._addr = 0xE000000000000000
self._size = 1024 * 1024 * 1024 # 1GB self._size = 1024 * 1024 * 1024 # 1GB
self._as_id = 0 self._as_id = 0
def interfaces(self) -> list[base.Interface]:
return [self._mem_if]
def add_if(self, interface: MemDeviceInterface) -> None: def add_if(self, interface: MemDeviceInterface) -> None:
utils_base.has_expected_type(obj=interface, expected_type=MemDeviceInterface) utils_base.has_expected_type(interface, MemDeviceInterface)
assert self._mem_if is None if self._mem_if:
raise Exception(
f"you overwrite MemDeviceInterface._mem_if ({self._mem_if.id()} -> {interface.id()}) "
)
self._mem_if = interface self._mem_if = interface
super().add_if(self._mem_if)
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
...@@ -76,8 +80,16 @@ class MemSimpleDevice(base.Component): ...@@ -76,8 +80,16 @@ class MemSimpleDevice(base.Component):
json_obj["size"] = self._size json_obj["size"] = self._size
json_obj["as_id"] = self._as_id json_obj["as_id"] = self._as_id
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: base.System, json_obj: dict) -> MemSimpleDevice:
# TODO instance = super().fromJSON(system, json_obj)
pass mem_if_id = int(utils_base.get_json_attr_top(json_obj, "mem_if"))
\ No newline at end of file addr = utils_base.get_json_attr_top(json_obj, "addr")
size = utils_base.get_json_attr_top(json_obj, "size")
as_id = utils_base.get_json_attr_top(json_obj, "as_id")
instance.mem_if = system.get_inf(mem_if_id)
instance.addr = addr
instance.size = size
instance.as_id = as_id
return instance
...@@ -20,28 +20,65 @@ ...@@ -20,28 +20,65 @@
# 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.
from __future__ import annotations
from simbricks.orchestration.system import base from simbricks.orchestration.system import base
from simbricks.orchestration.system import pcie from simbricks.orchestration.system import pcie
from simbricks.orchestration.system import eth from simbricks.orchestration.system import eth
from simbricks.orchestration.utils import base as utils_base
class SimplePCIeNIC(pcie.PCIeSimpleDevice, eth.EthSimpleNIC): class SimplePCIeNIC(base.Component):
def __init__(self, s: base.System) -> None: def __init__(self, s: base.System) -> None:
super().__init__(s) super().__init__(s)
self._ip: str | None = None
def interfaces(self) -> list[base.Interface]: self._eth_if: eth.EthInterface = eth.EthInterface(self)
return [self._pci_if, self._eth_if] super().add_if(self._eth_if)
self._pci_if: pcie.PCIeDeviceInterface = pcie.PCIeDeviceInterface(self)
super().add_if(self._pci_if)
def add_if(self, interface: eth.EthInterface | pcie.PCIeDeviceInterface) -> None: def add_if(self, interface: eth.EthInterface | pcie.PCIeDeviceInterface) -> None:
match interface: match interface:
case eth.EthInterface(): case eth.EthInterface():
eth.EthSimpleNIC.add_if(self, interface=interface) if hasattr(self, "_eth_if") and self._eth_if:
print(interface)
print(self._eth_if)
raise Exception(
f"you overwrite SimplePCIeNIC._eth_if ({self._eth_if.id()} -> {interface.id()}) "
)
self._eth_if = interface
case pcie.PCIeDeviceInterface(): case pcie.PCIeDeviceInterface():
pcie.PCIeSimpleDevice.add_if(self, interface=interface) if hasattr(self, "_pci_if") and self._pci_if:
raise Exception(
f"you overwrite SimplePCIeNIC._pci_if. ({self._pci_if.id()} -> {interface.id()})"
)
self._pci_if = interface
case _: case _:
raise Exception( raise Exception(
f"interface must have type EthInterface or PCIeDeviceInterface but has type {type(interface)}" f"interface must have type EthInterface or PCIeDeviceInterface but has type {type(interface)}"
) )
super().add_if(interface)
def add_ipv4(self, ip: str) -> None:
assert self._ip is None
self._ip = ip
def toJSON(self) -> dict:
json_obj = super().toJSON()
json_obj["ip"] = self._ip
json_obj["eth_if"] = self._eth_if.id()
json_obj["pci_if"] = self._pci_if.id()
return json_obj
@classmethod
def fromJSON(cls, system: base.System, json_obj: dict) -> SimplePCIeNIC:
instance = super().fromJSON(system, json_obj)
instance._ip = utils_base.get_json_attr_top(json_obj, "ip")
eth_inf_id = int(utils_base.get_json_attr_top(json_obj, "eth_if"))
instance._eth_if = system.get_inf(eth_inf_id)
inf_id = int(utils_base.get_json_attr_top(json_obj, "pci_if"))
instance._pci_if = system.get_inf(inf_id)
return instance
class IntelI40eNIC(SimplePCIeNIC): class IntelI40eNIC(SimplePCIeNIC):
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
# 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.
from __future__ import annotations
from simbricks.orchestration.system import base from simbricks.orchestration.system import base
from simbricks.orchestration.utils import base as utils_base from simbricks.orchestration.utils import base as utils_base
...@@ -57,19 +59,25 @@ class PCIeSimpleDevice(base.Component): ...@@ -57,19 +59,25 @@ class PCIeSimpleDevice(base.Component):
def __init__(self, s: base.System): def __init__(self, s: base.System):
super().__init__(s) super().__init__(s)
self._pci_if: PCIeDeviceInterface = PCIeDeviceInterface(self) self._pci_if: PCIeDeviceInterface = PCIeDeviceInterface(self)
super().add_if(self._pci_if)
def interfaces(self) -> list[base.Interface]:
return [self._pci_if]
def add_if(self, interface: PCIeDeviceInterface) -> None: def add_if(self, interface: PCIeDeviceInterface) -> None:
raise Exception("PCIeSimpleDevice already has PCI device interface") utils_base.has_expected_type(interface, PCIeDeviceInterface)
if self._pci_if:
raise Exception(
f"you overwrite PCIeSimpleDevice._pci_if. ({self._pci_if.id()} -> {interface.id()})"
)
self._pci_if = interface
super().add_if(interface)
def toJSON(self) -> dict: def toJSON(self) -> dict:
json_obj = super().toJSON() json_obj = super().toJSON()
json_obj["pci_if"] = self._pci_if.id() json_obj["pci_if"] = self._pci_if.id()
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, system: base.System, json_obj: dict) -> PCIeSimpleDevice:
# TODO instance = super().fromJSON(system=system, json_obj=json_obj)
pass inf_id = int(utils_base.get_json_attr_top(json_obj, "pci_if"))
\ No newline at end of file instance._pci_if = system.get_inf(inf_id)
return instance
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
import abc import abc
import itertools import itertools
import importlib
class IdObj(abc.ABC): class IdObj(abc.ABC):
...@@ -37,11 +38,13 @@ class IdObj(abc.ABC): ...@@ -37,11 +38,13 @@ class IdObj(abc.ABC):
json_obj = {} json_obj = {}
json_obj["id"] = self._id json_obj["id"] = self._id
return json_obj return json_obj
@staticmethod @classmethod
def fromJSON(json_obj): def fromJSON(cls, json_obj):
# TODO instance = cls.__new__(cls)
pass instance._id = get_json_attr_top(json_obj, "id")
return instance
def check_type(obj, expected_type) -> bool: def check_type(obj, expected_type) -> bool:
""" """
...@@ -57,9 +60,41 @@ def has_expected_type(obj, expected_type) -> None: ...@@ -57,9 +60,41 @@ 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: def has_attribute(obj, attr: str) -> None:
if not hasattr(obj, attr): if not hasattr(obj, attr):
raise Exception( raise Exception(f"obj of type {type(obj)} no attribute called {attr}")
f"obj of type {type(obj)} no attribute called {attr}"
)
def get_json_attr_top_or_none(json_obj: dict, attr: str) -> dict | None:
if attr in json_obj:
return json_obj[attr]
return None
def has_json_attr_top(json_obj: dict, attr: str) -> None:
if not attr in json_obj:
raise Exception(f"{json_obj} does not contain key {attr}")
def get_json_attr_top(json_obj: dict, attr: str) -> dict:
has_json_attr_top(json_obj, attr)
return json_obj[attr]
def get_cls_from_type_module(type_name: str, module_name: str):
# Import the module
module = importlib.import_module(module_name)
# Get the class from the module
cls = getattr(module, type_name)
return cls
def get_cls_by_json(json_obj: dict):
type_name = get_json_attr_top(json_obj, "type")
module_name = get_json_attr_top(json_obj, "module")
return get_cls_from_type_module(type_name, module_name)
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