diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py index 099ce043..fd1abc34 100644 --- a/daemon/core/gui/coreclient.py +++ b/daemon/core/gui/coreclient.py @@ -12,13 +12,15 @@ from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Set, Tuple import grpc -from core.api.grpc import client, core_pb2 -from core.api.grpc.configservices_pb2 import ConfigService, ConfigServiceConfig -from core.api.grpc.core_pb2 import CpuUsageEvent, Event -from core.api.grpc.emane_pb2 import EmaneModelConfig -from core.api.grpc.mobility_pb2 import MobilityConfig -from core.api.grpc.services_pb2 import NodeServiceData, ServiceConfig, ServiceFileConfig -from core.api.grpc.wlan_pb2 import WlanConfig +from core.api.grpc import ( + client, + configservices_pb2, + core_pb2, + emane_pb2, + mobility_pb2, + services_pb2, + wlan_pb2, +) from core.gui import appconfig from core.gui.appconfig import CoreServer, Observer from core.gui.dialogs.emaneinstall import EmaneInstallDialog @@ -33,6 +35,7 @@ from core.gui.interface import InterfaceManager from core.gui.nodeutils import NodeDraw, NodeUtils from core.gui.wrappers import ( ConfigOption, + ConfigService, ExceptionEvent, Hook, Interface, @@ -42,6 +45,7 @@ from core.gui.wrappers import ( MessageType, Node, NodeEvent, + NodeServiceData, NodeType, Position, SessionLocation, @@ -150,7 +154,7 @@ class CoreClient: for observer in self.app.guiconfig.observers: self.custom_observers[observer.name] = observer - def handle_events(self, event: Event) -> None: + def handle_events(self, event: core_pb2.Event) -> None: if event.source == GUI_SOURCE: return if event.session_id != self.session_id: @@ -288,7 +292,7 @@ class CoreClient: logging.debug("handling throughputs event: %s", event) self.app.after(0, self.app.canvas.set_throughputs, event) - def handle_cpu_event(self, event: CpuUsageEvent) -> None: + def handle_cpu_event(self, event: core_pb2.CpuUsageEvent) -> None: self.app.after(0, self.app.statusbar.set_cpu, event.usage) def handle_exception_event(self, event: ExceptionEvent) -> None: @@ -514,7 +518,7 @@ class CoreClient: # get config service informations response = self.client.get_config_services() for service in response.services: - self.config_services[service.name] = service + self.config_services[service.name] = ConfigService.from_proto(service) group_services = self.config_services_groups.setdefault( service.group, set() ) @@ -708,7 +712,7 @@ class CoreClient: logging.debug( "get node(%s) %s service, response: %s", node_id, service_name, response ) - return response.service + return NodeServiceData.from_proto(response.service) def set_node_service( self, @@ -742,7 +746,7 @@ class CoreClient: response, ) response = self.client.get_node_service(self.session_id, node_id, service_name) - return response.service + return NodeServiceData.from_proto(response.service) def get_node_service_file( self, node_id: int, service_name: str, file_name: str @@ -1000,7 +1004,7 @@ class CoreClient: self.links[edge.token] = edge logging.info("Add link between %s and %s", src_node.name, dst_node.name) - def get_wlan_configs_proto(self) -> List[WlanConfig]: + def get_wlan_configs_proto(self) -> List[wlan_pb2.WlanConfig]: configs = [] for canvas_node in self.canvas_nodes.values(): if canvas_node.core_node.type != NodeType.WIRELESS_LAN: @@ -1009,11 +1013,11 @@ class CoreClient: continue config = ConfigOption.to_dict(canvas_node.wlan_config) node_id = canvas_node.core_node.id - wlan_config = WlanConfig(node_id=node_id, config=config) + wlan_config = wlan_pb2.WlanConfig(node_id=node_id, config=config) configs.append(wlan_config) return configs - def get_mobility_configs_proto(self) -> List[MobilityConfig]: + def get_mobility_configs_proto(self) -> List[mobility_pb2.MobilityConfig]: configs = [] for canvas_node in self.canvas_nodes.values(): if canvas_node.core_node.type != NodeType.WIRELESS_LAN: @@ -1022,11 +1026,13 @@ class CoreClient: continue config = ConfigOption.to_dict(canvas_node.mobility_config) node_id = canvas_node.core_node.id - mobility_config = MobilityConfig(node_id=node_id, config=config) + mobility_config = mobility_pb2.MobilityConfig( + node_id=node_id, config=config + ) configs.append(mobility_config) return configs - def get_emane_model_configs_proto(self) -> List[EmaneModelConfig]: + def get_emane_model_configs_proto(self) -> List[emane_pb2.EmaneModelConfig]: configs = [] for canvas_node in self.canvas_nodes.values(): if canvas_node.core_node.type != NodeType.EMANE: @@ -1037,13 +1043,13 @@ class CoreClient: config = ConfigOption.to_dict(config) if iface_id is None: iface_id = -1 - config_proto = EmaneModelConfig( + config_proto = emane_pb2.EmaneModelConfig( node_id=node_id, iface_id=iface_id, model=model, config=config ) configs.append(config_proto) return configs - def get_service_configs_proto(self) -> List[ServiceConfig]: + def get_service_configs_proto(self) -> List[services_pb2.ServiceConfig]: configs = [] for canvas_node in self.canvas_nodes.values(): if not NodeUtils.is_container_node(canvas_node.core_node.type): @@ -1052,7 +1058,7 @@ class CoreClient: continue node_id = canvas_node.core_node.id for name, config in canvas_node.service_configs.items(): - config_proto = ServiceConfig( + config_proto = services_pb2.ServiceConfig( node_id=node_id, service=name, directories=config.dirs, @@ -1064,7 +1070,7 @@ class CoreClient: configs.append(config_proto) return configs - def get_service_file_configs_proto(self) -> List[ServiceFileConfig]: + def get_service_file_configs_proto(self) -> List[services_pb2.ServiceFileConfig]: configs = [] for canvas_node in self.canvas_nodes.values(): if not NodeUtils.is_container_node(canvas_node.core_node.type): @@ -1074,13 +1080,15 @@ class CoreClient: node_id = canvas_node.core_node.id for service, file_configs in canvas_node.service_file_configs.items(): for file, data in file_configs.items(): - config_proto = ServiceFileConfig( + config_proto = services_pb2.ServiceFileConfig( node_id=node_id, service=service, file=file, data=data ) configs.append(config_proto) return configs - def get_config_service_configs_proto(self) -> List[ConfigServiceConfig]: + def get_config_service_configs_proto( + self + ) -> List[configservices_pb2.ConfigServiceConfig]: config_service_protos = [] for canvas_node in self.canvas_nodes.values(): if not NodeUtils.is_container_node(canvas_node.core_node.type): @@ -1090,7 +1098,7 @@ class CoreClient: node_id = canvas_node.core_node.id for name, service_config in canvas_node.config_service_configs.items(): config = service_config.get("config", {}) - config_proto = ConfigServiceConfig( + config_proto = configservices_pb2.ConfigServiceConfig( node_id=node_id, name=name, templates=service_config["templates"], diff --git a/daemon/core/gui/dialogs/configserviceconfig.py b/daemon/core/gui/dialogs/configserviceconfig.py index 5a6a89a8..5463d88e 100644 --- a/daemon/core/gui/dialogs/configserviceconfig.py +++ b/daemon/core/gui/dialogs/configserviceconfig.py @@ -8,11 +8,10 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Set import grpc -from core.api.grpc.services_pb2 import ServiceValidationMode from core.gui.dialogs.dialog import Dialog from core.gui.themes import FRAME_PAD, PADX, PADY from core.gui.widgets import CodeText, ConfigFrame, ListboxScroll -from core.gui.wrappers import ConfigOption +from core.gui.wrappers import ConfigOption, ServiceValidationMode if TYPE_CHECKING: from core.gui.app import Application diff --git a/daemon/core/gui/dialogs/mobilityplayer.py b/daemon/core/gui/dialogs/mobilityplayer.py index 16aa8ea0..66833aff 100644 --- a/daemon/core/gui/dialogs/mobilityplayer.py +++ b/daemon/core/gui/dialogs/mobilityplayer.py @@ -4,11 +4,10 @@ from typing import TYPE_CHECKING, Dict, Optional import grpc -from core.api.grpc.mobility_pb2 import MobilityAction from core.gui.dialogs.dialog import Dialog from core.gui.images import ImageEnum from core.gui.themes import PADX, PADY -from core.gui.wrappers import ConfigOption, Node +from core.gui.wrappers import ConfigOption, MobilityAction, Node if TYPE_CHECKING: from core.gui.app import Application @@ -150,7 +149,7 @@ class MobilityPlayerDialog(Dialog): session_id = self.app.core.session_id try: self.app.core.client.mobility_action( - session_id, self.node.id, MobilityAction.START + session_id, self.node.id, MobilityAction.START.value ) except grpc.RpcError as e: self.app.show_grpc_exception("Mobility Error", e) @@ -160,7 +159,7 @@ class MobilityPlayerDialog(Dialog): session_id = self.app.core.session_id try: self.app.core.client.mobility_action( - session_id, self.node.id, MobilityAction.PAUSE + session_id, self.node.id, MobilityAction.PAUSE.value ) except grpc.RpcError as e: self.app.show_grpc_exception("Mobility Error", e) @@ -170,7 +169,7 @@ class MobilityPlayerDialog(Dialog): session_id = self.app.core.session_id try: self.app.core.client.mobility_action( - session_id, self.node.id, MobilityAction.STOP + session_id, self.node.id, MobilityAction.STOP.value ) except grpc.RpcError as e: self.app.show_grpc_exception("Mobility Error", e) diff --git a/daemon/core/gui/dialogs/serviceconfig.py b/daemon/core/gui/dialogs/serviceconfig.py index 4e615db0..c033cfdc 100644 --- a/daemon/core/gui/dialogs/serviceconfig.py +++ b/daemon/core/gui/dialogs/serviceconfig.py @@ -7,12 +7,12 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple import grpc from PIL.ImageTk import PhotoImage -from core.api.grpc.services_pb2 import NodeServiceData, ServiceValidationMode from core.gui.dialogs.copyserviceconfig import CopyServiceConfigDialog from core.gui.dialogs.dialog import Dialog from core.gui.images import ImageEnum, Images from core.gui.themes import FRAME_PAD, PADX, PADY from core.gui.widgets import CodeText, ListboxScroll +from core.gui.wrappers import NodeServiceData, ServiceValidationMode if TYPE_CHECKING: from core.gui.app import Application @@ -72,7 +72,7 @@ class ServiceConfigDialog(Dialog): self.service_file_data: Optional[CodeText] = None self.validation_period_entry: Optional[ttk.Entry] = None self.original_service_files: Dict[str, str] = {} - self.default_config: NodeServiceData = None + self.default_config: Optional[NodeServiceData] = None self.temp_service_files: Dict[str, str] = {} self.modified_files: Set[str] = set() self.has_error: bool = False diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index df6476c7..217389c0 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -6,7 +6,6 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple import grpc from PIL.ImageTk import PhotoImage -from core.api.grpc.services_pb2 import NodeServiceData from core.gui import themes from core.gui.dialogs.emaneconfig import EmaneConfigDialog from core.gui.dialogs.mobilityconfig import MobilityConfigDialog @@ -20,7 +19,7 @@ from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge from core.gui.graph.tooltip import CanvasTooltip from core.gui.images import ImageEnum from core.gui.nodeutils import ANTENNA_SIZE, NodeUtils -from core.gui.wrappers import ConfigOption, Interface, Node, NodeType +from core.gui.wrappers import ConfigOption, Interface, Node, NodeServiceData, NodeType if TYPE_CHECKING: from core.gui.app import Application diff --git a/daemon/core/gui/wrappers.py b/daemon/core/gui/wrappers.py index 4098a4df..5fb12837 100644 --- a/daemon/core/gui/wrappers.py +++ b/daemon/core/gui/wrappers.py @@ -2,7 +2,25 @@ from dataclasses import dataclass, field from enum import Enum from typing import Dict, List -from core.api.grpc import common_pb2, core_pb2 +from core.api.grpc import common_pb2, configservices_pb2, core_pb2, services_pb2 + + +class ConfigServiceValidationMode(Enum): + BLOCKING = 0 + NON_BLOCKING = 1 + TIMER = 2 + + +class ServiceValidationMode(Enum): + BLOCKING = 0 + NON_BLOCKING = 1 + TIMER = 2 + + +class MobilityAction(Enum): + START = 0 + PAUSE = 1 + STOP = 2 class ConfigOptionType(Enum): @@ -68,6 +86,68 @@ class MessageType(Enum): TTY = 64 +@dataclass +class ConfigService: + group: str + name: str + executables: List[str] + dependencies: List[str] + directories: List[str] + files: List[str] + startup: List[str] + validate: List[str] + shutdown: List[str] + validation_mode: ConfigServiceValidationMode + validation_timer: int + validation_period: float + + @classmethod + def from_proto(cls, proto: configservices_pb2.ConfigService) -> "ConfigService": + return ConfigService( + group=proto.group, + name=proto.name, + executables=proto.executables, + dependencies=proto.dependencies, + directories=proto.directories, + files=proto.files, + startup=proto.startup, + validate=proto.validate, + shutdown=proto.shutdown, + validation_mode=ConfigServiceValidationMode(proto.validation_mode), + validation_timer=proto.validation_timer, + validation_period=proto.validation_period, + ) + + +@dataclass +class NodeServiceData: + executables: List[str] + dependencies: List[str] + dirs: List[str] + configs: List[str] + startup: List[str] + validate: List[str] + validation_mode: ServiceValidationMode + validation_timer: int + shutdown: List[str] + meta: str + + @classmethod + def from_proto(cls, proto: services_pb2.NodeServiceData) -> "NodeServiceData": + return NodeServiceData( + executables=proto.executables, + dependencies=proto.dependencies, + dirs=proto.dirs, + configs=proto.configs, + startup=proto.startup, + validate=proto.validate, + validation_mode=proto.validation_mode, + validation_timer=proto.validation_timer, + shutdown=proto.shutdown, + meta=proto.meta, + ) + + @dataclass class BridgeThroughput: node_id: int @@ -119,15 +199,15 @@ class SessionLocation: scale: float @classmethod - def from_proto(cls, location: core_pb2.SessionLocation) -> "SessionLocation": + def from_proto(cls, proto: core_pb2.SessionLocation) -> "SessionLocation": return SessionLocation( - x=location.x, - y=location.y, - z=location.z, - lat=location.lat, - lon=location.lon, - alt=location.alt, - scale=location.scale, + x=proto.x, + y=proto.y, + z=proto.z, + lat=proto.lat, + lon=proto.lon, + alt=proto.alt, + scale=proto.scale, ) def to_proto(self) -> core_pb2.SessionLocation: @@ -154,16 +234,16 @@ class ExceptionEvent: @classmethod def from_proto( - cls, session_id: int, event: core_pb2.ExceptionEvent + cls, session_id: int, proto: core_pb2.ExceptionEvent ) -> "ExceptionEvent": return ExceptionEvent( session_id=session_id, - node_id=event.node_id, - level=ExceptionLevel(event.level), - source=event.source, - date=event.date, - text=event.text, - opaque=event.opaque, + node_id=proto.node_id, + level=ExceptionLevel(proto.level), + source=proto.source, + date=proto.date, + text=proto.text, + opaque=proto.opaque, ) @@ -190,14 +270,14 @@ class ConfigOption: return {k: v.value for k, v in config.items()} @classmethod - def from_proto(cls, option: common_pb2.ConfigOption) -> "ConfigOption": + def from_proto(cls, proto: common_pb2.ConfigOption) -> "ConfigOption": return ConfigOption( - label=option.label, - name=option.name, - value=option.value, - type=ConfigOptionType(option.type), - group=option.group, - select=option.select, + label=proto.label, + name=proto.name, + value=proto.value, + type=ConfigOptionType(proto.type), + group=proto.group, + select=proto.select, ) @@ -217,20 +297,20 @@ class Interface: net2_id: int = None @classmethod - def from_proto(cls, iface: core_pb2.Interface) -> "Interface": + def from_proto(cls, proto: core_pb2.Interface) -> "Interface": return Interface( - id=iface.id, - name=iface.name, - mac=iface.mac, - ip4=iface.ip4, - ip4_mask=iface.ip4_mask, - ip6=iface.ip6, - ip6_mask=iface.ip6_mask, - net_id=iface.net_id, - flow_id=iface.flow_id, - mtu=iface.mtu, - node_id=iface.node_id, - net2_id=iface.net2_id, + id=proto.id, + name=proto.name, + mac=proto.mac, + ip4=proto.ip4, + ip4_mask=proto.ip4_mask, + ip6=proto.ip6, + ip6_mask=proto.ip6_mask, + net_id=proto.net_id, + flow_id=proto.flow_id, + mtu=proto.mtu, + node_id=proto.node_id, + net2_id=proto.net2_id, ) def to_proto(self) -> core_pb2.Interface: @@ -264,18 +344,18 @@ class LinkOptions: unidirectional: bool = False @classmethod - def from_proto(cls, options: core_pb2.LinkOptions) -> "LinkOptions": + def from_proto(cls, proto: core_pb2.LinkOptions) -> "LinkOptions": return LinkOptions( - jitter=options.jitter, - key=options.key, - mburst=options.mburst, - mer=options.mer, - loss=options.loss, - bandwidth=options.bandwidth, - burst=options.burst, - delay=options.delay, - dup=options.dup, - unidirectional=options.unidirectional, + jitter=proto.jitter, + key=proto.key, + mburst=proto.mburst, + mer=proto.mer, + loss=proto.loss, + bandwidth=proto.bandwidth, + burst=proto.burst, + delay=proto.delay, + dup=proto.dup, + unidirectional=proto.unidirectional, ) def to_proto(self) -> core_pb2.LinkOptions: @@ -306,26 +386,26 @@ class Link: color: str = None @classmethod - def from_proto(cls, link: core_pb2.Link) -> "Link": + def from_proto(cls, proto: core_pb2.Link) -> "Link": iface1 = None - if link.HasField("iface1"): - iface1 = Interface.from_proto(link.iface1) + if proto.HasField("iface1"): + iface1 = Interface.from_proto(proto.iface1) iface2 = None - if link.HasField("iface2"): - iface2 = Interface.from_proto(link.iface2) + if proto.HasField("iface2"): + iface2 = Interface.from_proto(proto.iface2) options = None - if link.HasField("options"): - options = LinkOptions.from_proto(link.options) + if proto.HasField("options"): + options = LinkOptions.from_proto(proto.options) return Link( - type=LinkType(link.type), - node1_id=link.node1_id, - node2_id=link.node2_id, + type=LinkType(proto.type), + node1_id=proto.node1_id, + node2_id=proto.node2_id, iface1=iface1, iface2=iface2, options=options, - network_id=link.network_id, - label=link.label, - color=link.color, + network_id=proto.network_id, + label=proto.label, + color=proto.color, ) def to_proto(self) -> core_pb2.Link: @@ -360,13 +440,13 @@ class SessionSummary: dir: str @classmethod - def from_proto(cls, summary: core_pb2.SessionSummary) -> "SessionSummary": + def from_proto(cls, proto: core_pb2.SessionSummary) -> "SessionSummary": return SessionSummary( - id=summary.id, - state=SessionState(summary.state), - nodes=summary.nodes, - file=summary.file, - dir=summary.dir, + id=proto.id, + state=SessionState(proto.state), + nodes=proto.nodes, + file=proto.file, + dir=proto.dir, ) @@ -377,8 +457,8 @@ class Hook: data: str @classmethod - def from_proto(cls, hook: core_pb2.Hook) -> "Hook": - return Hook(state=SessionState(hook.state), file=hook.file, data=hook.data) + def from_proto(cls, proto: core_pb2.Hook) -> "Hook": + return Hook(state=SessionState(proto.state), file=proto.file, data=proto.data) def to_proto(self) -> core_pb2.Hook: return core_pb2.Hook(state=self.state.value, file=self.file, data=self.data) @@ -390,8 +470,8 @@ class Position: y: float @classmethod - def from_proto(cls, position: core_pb2.Position) -> "Position": - return Position(x=position.x, y=position.y) + def from_proto(cls, proto: core_pb2.Position) -> "Position": + return Position(x=proto.x, y=proto.y) def to_proto(self) -> core_pb2.Position: return core_pb2.Position(x=self.x, y=self.y) @@ -404,8 +484,8 @@ class Geo: alt: float = None @classmethod - def from_proto(cls, geo: core_pb2.Geo) -> "Geo": - return Geo(lat=geo.lat, lon=geo.lon, alt=geo.alt) + def from_proto(cls, proto: core_pb2.Geo) -> "Geo": + return Geo(lat=proto.lat, lon=proto.lon, alt=proto.alt) def to_proto(self) -> core_pb2.Geo: return core_pb2.Geo(lat=self.lat, lon=self.lon, alt=self.alt) @@ -429,22 +509,22 @@ class Node: channel: str = None @classmethod - def from_proto(cls, node: core_pb2.Node) -> "Node": + def from_proto(cls, proto: core_pb2.Node) -> "Node": return Node( - id=node.id, - name=node.name, - type=NodeType(node.type), - model=node.model, - position=Position.from_proto(node.position), - services=list(node.services), - config_services=list(node.config_services), - emane=node.emane, - icon=node.icon, - image=node.image, - server=node.server, - geo=Geo.from_proto(node.geo), - dir=node.dir, - channel=node.channel, + id=proto.id, + name=proto.name, + type=NodeType(proto.type), + model=proto.model, + position=Position.from_proto(proto.position), + services=list(proto.services), + config_services=list(proto.config_services), + emane=proto.emane, + icon=proto.icon, + image=proto.image, + server=proto.server, + geo=Geo.from_proto(proto.geo), + dir=proto.dir, + channel=proto.channel, ) def to_proto(self) -> core_pb2.Node: @@ -471,10 +551,10 @@ class LinkEvent: link: Link @classmethod - def from_proto(cls, event: core_pb2.LinkEvent) -> "LinkEvent": + def from_proto(cls, proto: core_pb2.LinkEvent) -> "LinkEvent": return LinkEvent( - message_type=MessageType(event.message_type), - link=Link.from_proto(event.link), + message_type=MessageType(proto.message_type), + link=Link.from_proto(proto.link), ) @@ -484,8 +564,8 @@ class NodeEvent: node: Node @classmethod - def from_proto(cls, event: core_pb2.NodeEvent) -> "NodeEvent": + def from_proto(cls, proto: core_pb2.NodeEvent) -> "NodeEvent": return NodeEvent( - message_type=MessageType(event.message_type), - node=Node.from_proto(event.node), + message_type=MessageType(proto.message_type), + node=Node.from_proto(proto.node), )