pygui: added wrappers for most usages of protobufs within pygui
This commit is contained in:
parent
154fa8b77d
commit
77f6577bce
23 changed files with 475 additions and 173 deletions
|
@ -13,27 +13,8 @@ from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Set, Tuple
|
|||
import grpc
|
||||
|
||||
from core.api.grpc import client
|
||||
from core.api.grpc.common_pb2 import ConfigOption
|
||||
from core.api.grpc.configservices_pb2 import ConfigService, ConfigServiceConfig
|
||||
from core.api.grpc.core_pb2 import (
|
||||
CpuUsageEvent,
|
||||
Event,
|
||||
ExceptionEvent,
|
||||
Interface,
|
||||
Link,
|
||||
LinkEvent,
|
||||
LinkType,
|
||||
MessageType,
|
||||
Node,
|
||||
NodeEvent,
|
||||
NodeType,
|
||||
Position,
|
||||
SessionLocation,
|
||||
SessionState,
|
||||
StartSessionResponse,
|
||||
StopSessionResponse,
|
||||
ThroughputsEvent,
|
||||
)
|
||||
from core.api.grpc.core_pb2 import CpuUsageEvent, Event, ThroughputsEvent
|
||||
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
|
||||
|
@ -50,7 +31,22 @@ from core.gui.graph.shape import AnnotationData, Shape
|
|||
from core.gui.graph.shapeutils import ShapeType
|
||||
from core.gui.interface import InterfaceManager
|
||||
from core.gui.nodeutils import NodeDraw, NodeUtils
|
||||
from core.gui.wrappers import Hook
|
||||
from core.gui.wrappers import (
|
||||
ConfigOption,
|
||||
ExceptionEvent,
|
||||
Hook,
|
||||
Interface,
|
||||
Link,
|
||||
LinkEvent,
|
||||
LinkType,
|
||||
MessageType,
|
||||
Node,
|
||||
NodeEvent,
|
||||
NodeType,
|
||||
Position,
|
||||
SessionLocation,
|
||||
SessionState,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -165,12 +161,13 @@ class CoreClient:
|
|||
return
|
||||
|
||||
if event.HasField("link_event"):
|
||||
self.app.after(0, self.handle_link_event, event.link_event)
|
||||
link_event = LinkEvent.from_proto(event.link_event)
|
||||
self.app.after(0, self.handle_link_event, link_event)
|
||||
elif event.HasField("session_event"):
|
||||
logging.info("session event: %s", event)
|
||||
session_event = event.session_event
|
||||
if session_event.event <= SessionState.SHUTDOWN:
|
||||
self.state = event.session_event.event
|
||||
if session_event.event <= SessionState.SHUTDOWN.value:
|
||||
self.state = SessionState(session_event.event)
|
||||
elif session_event.event in {7, 8, 9}:
|
||||
node_id = session_event.node_id
|
||||
dialog = self.mobility_players.get(node_id)
|
||||
|
@ -184,10 +181,12 @@ class CoreClient:
|
|||
else:
|
||||
logging.warning("unknown session event: %s", session_event)
|
||||
elif event.HasField("node_event"):
|
||||
self.app.after(0, self.handle_node_event, event.node_event)
|
||||
node_event = NodeEvent.from_proto(event.node_event)
|
||||
self.app.after(0, self.handle_node_event, node_event)
|
||||
elif event.HasField("config_event"):
|
||||
logging.info("config event: %s", event)
|
||||
elif event.HasField("exception_event"):
|
||||
event = ExceptionEvent.from_proto(event.session_id, event.exception_event)
|
||||
self.handle_exception_event(event)
|
||||
else:
|
||||
logging.info("unhandled event: %s", event)
|
||||
|
@ -307,7 +306,7 @@ class CoreClient:
|
|||
try:
|
||||
response = self.client.get_session(self.session_id)
|
||||
session = response.session
|
||||
self.state = session.state
|
||||
self.state = SessionState(session.state)
|
||||
self.handling_events = self.client.events(
|
||||
self.session_id, self.handle_events
|
||||
)
|
||||
|
@ -324,7 +323,7 @@ class CoreClient:
|
|||
# get location
|
||||
if query_location:
|
||||
response = self.client.get_session_location(self.session_id)
|
||||
self.location = response.location
|
||||
self.location = SessionLocation.from_proto(response.location)
|
||||
|
||||
# get emane models
|
||||
response = self.client.get_emane_models(self.session_id)
|
||||
|
@ -338,20 +337,22 @@ class CoreClient:
|
|||
|
||||
# get emane config
|
||||
response = self.client.get_emane_config(self.session_id)
|
||||
self.emane_config = response.config
|
||||
self.emane_config = ConfigOption.from_dict(response.config)
|
||||
|
||||
# update interface manager
|
||||
self.ifaces_manager.joined(session.links)
|
||||
|
||||
# draw session
|
||||
self.app.canvas.reset_and_redraw(session)
|
||||
nodes = [Node.from_proto(x) for x in session.nodes]
|
||||
links = [Link.from_proto(x) for x in session.links]
|
||||
self.app.canvas.reset_and_redraw(nodes, links)
|
||||
|
||||
# get mobility configs
|
||||
response = self.client.get_mobility_configs(self.session_id)
|
||||
for node_id in response.configs:
|
||||
config = response.configs[node_id].config
|
||||
canvas_node = self.canvas_nodes[node_id]
|
||||
canvas_node.mobility_config = dict(config)
|
||||
canvas_node.mobility_config = ConfigOption.from_dict(config)
|
||||
|
||||
# get emane model config
|
||||
response = self.client.get_emane_model_configs(self.session_id)
|
||||
|
@ -360,16 +361,16 @@ class CoreClient:
|
|||
if config.iface_id != -1:
|
||||
iface_id = config.iface_id
|
||||
canvas_node = self.canvas_nodes[config.node_id]
|
||||
canvas_node.emane_model_configs[(config.model, iface_id)] = dict(
|
||||
config.config
|
||||
)
|
||||
canvas_node.emane_model_configs[
|
||||
(config.model, iface_id)
|
||||
] = ConfigOption.from_dict(config.config)
|
||||
|
||||
# get wlan configurations
|
||||
response = self.client.get_wlan_configs(self.session_id)
|
||||
for _id in response.configs:
|
||||
mapped_config = response.configs[_id]
|
||||
canvas_node = self.canvas_nodes[_id]
|
||||
canvas_node.wlan_config = dict(mapped_config.config)
|
||||
canvas_node.wlan_config = ConfigOption.from_dict(mapped_config.config)
|
||||
|
||||
# get service configurations
|
||||
response = self.client.get_node_service_configs(self.session_id)
|
||||
|
@ -501,7 +502,6 @@ class CoreClient:
|
|||
"""
|
||||
try:
|
||||
self.client.connect()
|
||||
self.setup_cpu_usage()
|
||||
|
||||
# get service information
|
||||
response = self.client.get_services()
|
||||
|
@ -546,8 +546,9 @@ class CoreClient:
|
|||
|
||||
def edit_node(self, core_node: Node) -> None:
|
||||
try:
|
||||
position = core_node.position.to_proto()
|
||||
self.client.edit_node(
|
||||
self.session_id, core_node.id, core_node.position, source=GUI_SOURCE
|
||||
self.session_id, core_node.id, position, source=GUI_SOURCE
|
||||
)
|
||||
except grpc.RpcError as e:
|
||||
self.app.show_grpc_exception("Edit Node Error", e)
|
||||
|
@ -556,18 +557,17 @@ class CoreClient:
|
|||
for server in self.servers.values():
|
||||
self.client.add_session_server(self.session_id, server.name, server.address)
|
||||
|
||||
def start_session(self) -> StartSessionResponse:
|
||||
def start_session(self) -> Tuple[bool, List[str]]:
|
||||
self.ifaces_manager.reset_mac()
|
||||
nodes = [x.core_node for x in self.canvas_nodes.values()]
|
||||
nodes = [x.core_node.to_proto() for x in self.canvas_nodes.values()]
|
||||
links = []
|
||||
for edge in self.links.values():
|
||||
link = Link()
|
||||
link.CopyFrom(edge.link)
|
||||
if link.HasField("iface1") and not link.iface1.mac:
|
||||
link = edge.link
|
||||
if link.iface1 and not link.iface1.mac:
|
||||
link.iface1.mac = self.ifaces_manager.next_mac()
|
||||
if link.HasField("iface2") and not link.iface2.mac:
|
||||
if link.iface2 and not link.iface2.mac:
|
||||
link.iface2.mac = self.ifaces_manager.next_mac()
|
||||
links.append(link)
|
||||
links.append(link.to_proto())
|
||||
wlan_configs = self.get_wlan_configs_proto()
|
||||
mobility_configs = self.get_mobility_configs_proto()
|
||||
emane_model_configs = self.get_emane_model_configs_proto()
|
||||
|
@ -582,14 +582,15 @@ class CoreClient:
|
|||
emane_config = {x: self.emane_config[x].value for x in self.emane_config}
|
||||
else:
|
||||
emane_config = None
|
||||
response = StartSessionResponse(result=False)
|
||||
result = False
|
||||
exceptions = []
|
||||
try:
|
||||
self.send_servers()
|
||||
response = self.client.start_session(
|
||||
self.session_id,
|
||||
nodes,
|
||||
links,
|
||||
self.location,
|
||||
self.location.to_proto(),
|
||||
hooks,
|
||||
emane_config,
|
||||
emane_model_configs,
|
||||
|
@ -605,20 +606,23 @@ class CoreClient:
|
|||
)
|
||||
if response.result:
|
||||
self.set_metadata()
|
||||
result = response.result
|
||||
exceptions = response.exceptions
|
||||
except grpc.RpcError as e:
|
||||
self.app.show_grpc_exception("Start Session Error", e)
|
||||
return response
|
||||
return result, exceptions
|
||||
|
||||
def stop_session(self, session_id: int = None) -> StopSessionResponse:
|
||||
def stop_session(self, session_id: int = None) -> bool:
|
||||
if not session_id:
|
||||
session_id = self.session_id
|
||||
response = StopSessionResponse(result=False)
|
||||
result = False
|
||||
try:
|
||||
response = self.client.stop_session(session_id)
|
||||
logging.info("stopped session(%s), result: %s", session_id, response)
|
||||
result = response.result
|
||||
except grpc.RpcError as e:
|
||||
self.app.show_grpc_exception("Stop Session Error", e)
|
||||
return response
|
||||
return result
|
||||
|
||||
def show_mobility_players(self) -> None:
|
||||
for canvas_node in self.canvas_nodes.values():
|
||||
|
@ -920,7 +924,7 @@ class CoreClient:
|
|||
)
|
||||
return node
|
||||
|
||||
def deleted_graph_nodes(self, canvas_nodes: List[Node]) -> None:
|
||||
def deleted_graph_nodes(self, canvas_nodes: List[CanvasNode]) -> None:
|
||||
"""
|
||||
remove the nodes selected by the user and anything related to that node
|
||||
such as link, configurations, interfaces
|
||||
|
@ -951,13 +955,7 @@ class CoreClient:
|
|||
ip6=ip6,
|
||||
ip6_mask=ip6_mask,
|
||||
)
|
||||
logging.info(
|
||||
"create node(%s) interface(%s) IPv4(%s) IPv6(%s)",
|
||||
node.name,
|
||||
iface.name,
|
||||
iface.ip4,
|
||||
iface.ip6,
|
||||
)
|
||||
logging.info("create node(%s) interface(%s)", node.name, iface)
|
||||
return iface
|
||||
|
||||
def create_link(
|
||||
|
@ -1010,8 +1008,7 @@ class CoreClient:
|
|||
continue
|
||||
if not canvas_node.wlan_config:
|
||||
continue
|
||||
config = canvas_node.wlan_config
|
||||
config = {x: config[x].value for x in config}
|
||||
config = ConfigOption.to_dict(canvas_node.wlan_config)
|
||||
node_id = canvas_node.core_node.id
|
||||
wlan_config = WlanConfig(node_id=node_id, config=config)
|
||||
configs.append(wlan_config)
|
||||
|
@ -1024,8 +1021,7 @@ class CoreClient:
|
|||
continue
|
||||
if not canvas_node.mobility_config:
|
||||
continue
|
||||
config = canvas_node.mobility_config
|
||||
config = {x: config[x].value for x in config}
|
||||
config = ConfigOption.to_dict(canvas_node.mobility_config)
|
||||
node_id = canvas_node.core_node.id
|
||||
mobility_config = MobilityConfig(node_id=node_id, config=config)
|
||||
configs.append(mobility_config)
|
||||
|
@ -1039,7 +1035,7 @@ class CoreClient:
|
|||
node_id = canvas_node.core_node.id
|
||||
for key, config in canvas_node.emane_model_configs.items():
|
||||
model, iface_id = key
|
||||
config = {x: config[x].value for x in config}
|
||||
config = ConfigOption.to_dict(config)
|
||||
if iface_id is None:
|
||||
iface_id = -1
|
||||
config_proto = EmaneModelConfig(
|
||||
|
@ -1116,7 +1112,7 @@ class CoreClient:
|
|||
node_id,
|
||||
config,
|
||||
)
|
||||
return dict(config)
|
||||
return ConfigOption.from_dict(config)
|
||||
|
||||
def get_mobility_config(self, node_id: int) -> Dict[str, ConfigOption]:
|
||||
response = self.client.get_mobility_config(self.session_id, node_id)
|
||||
|
@ -1126,7 +1122,7 @@ class CoreClient:
|
|||
node_id,
|
||||
config,
|
||||
)
|
||||
return dict(config)
|
||||
return ConfigOption.from_dict(config)
|
||||
|
||||
def get_emane_model_config(
|
||||
self, node_id: int, model: str, iface_id: int = None
|
||||
|
@ -1145,7 +1141,7 @@ class CoreClient:
|
|||
iface_id,
|
||||
config,
|
||||
)
|
||||
return dict(config)
|
||||
return ConfigOption.from_dict(config)
|
||||
|
||||
def execute_script(self, script) -> None:
|
||||
response = self.client.execute_script(script)
|
||||
|
|
|
@ -5,10 +5,10 @@ import tkinter as tk
|
|||
from tkinter import ttk
|
||||
from typing import TYPE_CHECKING, Dict, Optional
|
||||
|
||||
from core.api.grpc.core_pb2 import ExceptionEvent, ExceptionLevel
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
from core.gui.themes import PADX, PADY
|
||||
from core.gui.widgets import CodeText
|
||||
from core.gui.wrappers import ExceptionEvent, ExceptionLevel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -49,9 +49,8 @@ class AlertsDialog(Dialog):
|
|||
self.tree.heading("source", text="Source")
|
||||
self.tree.bind("<<TreeviewSelect>>", self.click_select)
|
||||
|
||||
for alarm in self.app.statusbar.core_alarms:
|
||||
exception = alarm.exception_event
|
||||
level_name = ExceptionLevel.Enum.Name(exception.level)
|
||||
for exception in self.app.statusbar.core_alarms:
|
||||
level_name = exception.level.name
|
||||
node_id = exception.node_id if exception.node_id else ""
|
||||
insert_id = self.tree.insert(
|
||||
"",
|
||||
|
@ -60,21 +59,21 @@ class AlertsDialog(Dialog):
|
|||
values=(
|
||||
exception.date,
|
||||
level_name,
|
||||
alarm.session_id,
|
||||
exception.session_id,
|
||||
node_id,
|
||||
exception.source,
|
||||
),
|
||||
tags=(level_name,),
|
||||
)
|
||||
self.alarm_map[insert_id] = alarm
|
||||
self.alarm_map[insert_id] = exception
|
||||
|
||||
error_name = ExceptionLevel.Enum.Name(ExceptionLevel.ERROR)
|
||||
error_name = ExceptionLevel.ERROR.name
|
||||
self.tree.tag_configure(error_name, background="#ff6666")
|
||||
fatal_name = ExceptionLevel.Enum.Name(ExceptionLevel.FATAL)
|
||||
fatal_name = ExceptionLevel.FATAL.name
|
||||
self.tree.tag_configure(fatal_name, background="#d9d9d9")
|
||||
warning_name = ExceptionLevel.Enum.Name(ExceptionLevel.WARNING)
|
||||
warning_name = ExceptionLevel.WARNING.name
|
||||
self.tree.tag_configure(warning_name, background="#ffff99")
|
||||
notice_name = ExceptionLevel.Enum.Name(ExceptionLevel.NOTICE)
|
||||
notice_name = ExceptionLevel.NOTICE.name
|
||||
self.tree.tag_configure(notice_name, background="#85e085")
|
||||
|
||||
yscrollbar = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview)
|
||||
|
@ -108,8 +107,8 @@ class AlertsDialog(Dialog):
|
|||
|
||||
def click_select(self, event: tk.Event) -> None:
|
||||
current = self.tree.selection()[0]
|
||||
alarm = self.alarm_map[current]
|
||||
exception = self.alarm_map[current]
|
||||
self.codetext.text.config(state=tk.NORMAL)
|
||||
self.codetext.text.delete(1.0, tk.END)
|
||||
self.codetext.text.insert(1.0, alarm.exception_event.text)
|
||||
self.codetext.text.insert(1.0, exception.text)
|
||||
self.codetext.text.config(state=tk.DISABLED)
|
||||
|
|
|
@ -8,11 +8,11 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Set
|
|||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc.common_pb2 import ConfigOption
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -99,7 +99,7 @@ class ConfigServiceConfigDialog(Dialog):
|
|||
service_config = self.canvas_node.config_service_configs.get(
|
||||
self.service_name, {}
|
||||
)
|
||||
self.config = response.config
|
||||
self.config = ConfigOption.from_dict(response.config)
|
||||
self.default_config = {x.name: x.value for x in self.config.values()}
|
||||
custom_config = service_config.get("config")
|
||||
if custom_config:
|
||||
|
|
|
@ -8,12 +8,11 @@ from typing import TYPE_CHECKING, Dict, List, Optional
|
|||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc.common_pb2 import ConfigOption
|
||||
from core.api.grpc.core_pb2 import Node
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
from core.gui.images import ImageEnum, Images
|
||||
from core.gui.themes import PADX, PADY
|
||||
from core.gui.widgets import ConfigFrame
|
||||
from core.gui.wrappers import ConfigOption, Node
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
|
|
@ -5,11 +5,11 @@ import tkinter as tk
|
|||
from tkinter import ttk
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from core.gui import validation
|
||||
from core.gui.dialogs.colorpicker import ColorPickerDialog
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
from core.gui.themes import PADX, PADY
|
||||
from core.gui.wrappers import Interface, Link, LinkOptions
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -21,7 +21,7 @@ def get_int(var: tk.StringVar) -> Optional[int]:
|
|||
if value != "":
|
||||
return int(value)
|
||||
else:
|
||||
return None
|
||||
return 0
|
||||
|
||||
|
||||
def get_float(var: tk.StringVar) -> Optional[float]:
|
||||
|
@ -29,14 +29,15 @@ def get_float(var: tk.StringVar) -> Optional[float]:
|
|||
if value != "":
|
||||
return float(value)
|
||||
else:
|
||||
return None
|
||||
return 0.0
|
||||
|
||||
|
||||
class LinkConfigurationDialog(Dialog):
|
||||
def __init__(self, app: "Application", edge: "CanvasEdge") -> None:
|
||||
super().__init__(app, "Link Configuration")
|
||||
self.edge: "CanvasEdge" = edge
|
||||
self.is_symmetric: bool = edge.link.options.unidirectional is False
|
||||
|
||||
self.is_symmetric: bool = edge.link.is_symmetric()
|
||||
if self.is_symmetric:
|
||||
symmetry_var = tk.StringVar(value=">>")
|
||||
else:
|
||||
|
@ -223,32 +224,32 @@ class LinkConfigurationDialog(Dialog):
|
|||
delay = get_int(self.delay)
|
||||
duplicate = get_int(self.duplicate)
|
||||
loss = get_float(self.loss)
|
||||
options = core_pb2.LinkOptions(
|
||||
options = LinkOptions(
|
||||
bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, loss=loss
|
||||
)
|
||||
link.options.CopyFrom(options)
|
||||
link.options = options
|
||||
|
||||
iface1_id = None
|
||||
if link.HasField("iface1"):
|
||||
if link.iface1:
|
||||
iface1_id = link.iface1.id
|
||||
iface2_id = None
|
||||
if link.HasField("iface2"):
|
||||
if link.iface2:
|
||||
iface2_id = link.iface2.id
|
||||
|
||||
if not self.is_symmetric:
|
||||
link.options.unidirectional = True
|
||||
asym_iface1 = None
|
||||
if iface1_id:
|
||||
asym_iface1 = core_pb2.Interface(id=iface1_id)
|
||||
asym_iface1 = Interface(id=iface1_id)
|
||||
asym_iface2 = None
|
||||
if iface2_id:
|
||||
asym_iface2 = core_pb2.Interface(id=iface2_id)
|
||||
asym_iface2 = Interface(id=iface2_id)
|
||||
down_bandwidth = get_int(self.down_bandwidth)
|
||||
down_jitter = get_int(self.down_jitter)
|
||||
down_delay = get_int(self.down_delay)
|
||||
down_duplicate = get_int(self.down_duplicate)
|
||||
down_loss = get_float(self.down_loss)
|
||||
options = core_pb2.LinkOptions(
|
||||
options = LinkOptions(
|
||||
bandwidth=down_bandwidth,
|
||||
jitter=down_jitter,
|
||||
delay=down_delay,
|
||||
|
@ -256,7 +257,7 @@ class LinkConfigurationDialog(Dialog):
|
|||
loss=down_loss,
|
||||
unidirectional=True,
|
||||
)
|
||||
self.edge.asymmetric_link = core_pb2.Link(
|
||||
self.edge.asymmetric_link = Link(
|
||||
node1_id=link.node2_id,
|
||||
node2_id=link.node1_id,
|
||||
iface1=asym_iface1,
|
||||
|
@ -267,7 +268,7 @@ class LinkConfigurationDialog(Dialog):
|
|||
link.options.unidirectional = False
|
||||
self.edge.asymmetric_link = None
|
||||
|
||||
if self.app.core.is_runtime() and link.HasField("options"):
|
||||
if self.app.core.is_runtime() and link.options:
|
||||
session_id = self.app.core.session_id
|
||||
self.app.core.client.edit_link(
|
||||
session_id,
|
||||
|
@ -316,7 +317,7 @@ class LinkConfigurationDialog(Dialog):
|
|||
color = self.app.canvas.itemcget(self.edge.id, "fill")
|
||||
self.color.set(color)
|
||||
link = self.edge.link
|
||||
if link.HasField("options"):
|
||||
if link.options:
|
||||
self.bandwidth.set(str(link.options.bandwidth))
|
||||
self.jitter.set(str(link.options.jitter))
|
||||
self.duplicate.set(str(link.options.dup))
|
||||
|
|
|
@ -6,11 +6,10 @@ from typing import TYPE_CHECKING, Dict, Optional
|
|||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc.common_pb2 import ConfigOption
|
||||
from core.api.grpc.core_pb2 import Node
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
from core.gui.themes import PADX, PADY
|
||||
from core.gui.widgets import ConfigFrame
|
||||
from core.gui.wrappers import ConfigOption, Node
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
|
|
@ -4,12 +4,11 @@ from typing import TYPE_CHECKING, Dict, Optional
|
|||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc.common_pb2 import ConfigOption
|
||||
from core.api.grpc.core_pb2 import Node
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
|
|
@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Dict, Optional
|
|||
import netaddr
|
||||
from PIL.ImageTk import PhotoImage
|
||||
|
||||
from core.api.grpc.core_pb2 import Node
|
||||
from core.gui import nodeutils, validation
|
||||
from core.gui.appconfig import ICONS_PATH
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
|
@ -16,6 +15,7 @@ from core.gui.images import Images
|
|||
from core.gui.nodeutils import NodeUtils
|
||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||
from core.gui.widgets import ListboxScroll, image_chooser
|
||||
from core.gui.wrappers import Node
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
|
|
@ -5,10 +5,10 @@ from typing import TYPE_CHECKING, Dict, Optional
|
|||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc.common_pb2 import ConfigOption
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
from core.gui.themes import PADX, PADY
|
||||
from core.gui.widgets import ConfigFrame
|
||||
from core.gui.wrappers import ConfigOption
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -28,7 +28,7 @@ class SessionOptionsDialog(Dialog):
|
|||
try:
|
||||
session_id = self.app.core.session_id
|
||||
response = self.app.core.client.get_session_options(session_id)
|
||||
return response.config
|
||||
return ConfigOption.from_dict(response.config)
|
||||
except grpc.RpcError as e:
|
||||
self.app.show_grpc_exception("Get Session Options Error", e)
|
||||
self.has_error = True
|
||||
|
|
|
@ -5,12 +5,11 @@ from typing import TYPE_CHECKING, List, Optional
|
|||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from core.api.grpc.core_pb2 import SessionSummary
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
from core.gui.images import ImageEnum, Images
|
||||
from core.gui.task import ProgressTask
|
||||
from core.gui.themes import PADX, PADY
|
||||
from core.gui.wrappers import SessionState, SessionSummary
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -33,7 +32,7 @@ class SessionsDialog(Dialog):
|
|||
try:
|
||||
response = self.app.core.client.get_sessions()
|
||||
logging.info("sessions: %s", response)
|
||||
return response.sessions
|
||||
return [SessionSummary.from_proto(x) for x in response.sessions]
|
||||
except grpc.RpcError as e:
|
||||
self.app.show_grpc_exception("Get Sessions Error", e)
|
||||
self.destroy()
|
||||
|
@ -82,7 +81,7 @@ class SessionsDialog(Dialog):
|
|||
self.tree.heading("nodes", text="Node Count")
|
||||
|
||||
for index, session in enumerate(self.sessions):
|
||||
state_name = core_pb2.SessionState.Enum.Name(session.state)
|
||||
state_name = SessionState(session.state).name
|
||||
self.tree.insert(
|
||||
"",
|
||||
tk.END,
|
||||
|
|
|
@ -3,11 +3,10 @@ from typing import TYPE_CHECKING, Dict, Optional
|
|||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc.common_pb2 import ConfigOption
|
||||
from core.api.grpc.core_pb2 import Node
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
from core.gui.themes import PADX, PADY
|
||||
from core.gui.widgets import ConfigFrame
|
||||
from core.gui.wrappers import ConfigOption, Node
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import tkinter as tk
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from core.api.grpc.core_pb2 import Interface
|
||||
from core.gui.frames.base import DetailsFrame, InfoFrameBase
|
||||
from core.gui.utils import bandwidth_text
|
||||
from core.gui.wrappers import Interface
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -62,7 +62,7 @@ class EdgeInfoFrame(InfoFrameBase):
|
|||
ip6 = f"{iface2.ip6}/{iface2.ip6_mask}" if iface2.ip6 else ""
|
||||
frame.add_detail("IP6", ip6)
|
||||
|
||||
if link.HasField("options"):
|
||||
if link.options:
|
||||
frame.add_separator()
|
||||
bandwidth = bandwidth_text(options.bandwidth)
|
||||
frame.add_detail("Bandwidth", bandwidth)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
from core.api.grpc.core_pb2 import NodeType
|
||||
from core.gui.frames.base import DetailsFrame, InfoFrameBase
|
||||
from core.gui.nodeutils import NodeUtils
|
||||
from core.gui.wrappers import NodeType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
|
|
@ -3,14 +3,13 @@ import math
|
|||
import tkinter as tk
|
||||
from typing import TYPE_CHECKING, Optional, Tuple
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from core.api.grpc.core_pb2 import Interface, Link
|
||||
from core.gui import themes
|
||||
from core.gui.dialogs.linkconfig import LinkConfigurationDialog
|
||||
from core.gui.frames.link import EdgeInfoFrame, WirelessEdgeInfoFrame
|
||||
from core.gui.graph import tags
|
||||
from core.gui.nodeutils import NodeUtils
|
||||
from core.gui.utils import bandwidth_text
|
||||
from core.gui.wrappers import Interface, Link
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.graph.graph import CanvasGraph
|
||||
|
@ -305,7 +304,7 @@ class CanvasEdge(Edge):
|
|||
self.link = link
|
||||
self.draw_labels()
|
||||
|
||||
def iface_label(self, iface: core_pb2.Interface) -> str:
|
||||
def iface_label(self, iface: Interface) -> str:
|
||||
label = ""
|
||||
if iface.name and self.canvas.show_iface_names.get():
|
||||
label = f"{iface.name}"
|
||||
|
@ -319,10 +318,10 @@ class CanvasEdge(Edge):
|
|||
|
||||
def create_node_labels(self) -> Tuple[str, str]:
|
||||
label1 = None
|
||||
if self.link.HasField("iface1"):
|
||||
if self.link.iface1:
|
||||
label1 = self.iface_label(self.link.iface1)
|
||||
label2 = None
|
||||
if self.link.HasField("iface2"):
|
||||
if self.link.iface2:
|
||||
label2 = self.iface_label(self.link.iface2)
|
||||
return label1, label2
|
||||
|
||||
|
@ -417,6 +416,8 @@ class CanvasEdge(Edge):
|
|||
dialog.show()
|
||||
|
||||
def draw_link_options(self):
|
||||
if not self.link.options:
|
||||
return
|
||||
options = self.link.options
|
||||
lines = []
|
||||
bandwidth = options.bandwidth
|
||||
|
|
|
@ -7,14 +7,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
|
|||
from PIL import Image
|
||||
from PIL.ImageTk import PhotoImage
|
||||
|
||||
from core.api.grpc.core_pb2 import (
|
||||
Interface,
|
||||
Link,
|
||||
LinkType,
|
||||
Node,
|
||||
Session,
|
||||
ThroughputsEvent,
|
||||
)
|
||||
from core.api.grpc.core_pb2 import ThroughputsEvent
|
||||
from core.gui.dialogs.shapemod import ShapeDialog
|
||||
from core.gui.graph import tags
|
||||
from core.gui.graph.edges import (
|
||||
|
@ -30,6 +23,7 @@ from core.gui.graph.shape import Shape
|
|||
from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker
|
||||
from core.gui.images import ImageEnum, TypeToImage
|
||||
from core.gui.nodeutils import NodeDraw, NodeUtils
|
||||
from core.gui.wrappers import Interface, Link, LinkType, Node
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -134,12 +128,7 @@ class CanvasGraph(tk.Canvas):
|
|||
)
|
||||
self.configure(scrollregion=self.bbox(tk.ALL))
|
||||
|
||||
def reset_and_redraw(self, session: Session) -> None:
|
||||
"""
|
||||
Reset the private variables CanvasGraph object, redraw nodes given the new grpc
|
||||
client.
|
||||
:param session: session to draw
|
||||
"""
|
||||
def reset_and_redraw(self, nodes: List[Node], links: List[Link]) -> None:
|
||||
# reset view options to default state
|
||||
self.show_node_labels.set(True)
|
||||
self.show_link_labels.set(True)
|
||||
|
@ -164,7 +153,7 @@ class CanvasGraph(tk.Canvas):
|
|||
self.wireless_edges.clear()
|
||||
self.wireless_network.clear()
|
||||
self.drawing_edge = None
|
||||
self.draw_session(session)
|
||||
self.draw_session(nodes, links)
|
||||
|
||||
def setup_bindings(self) -> None:
|
||||
"""
|
||||
|
@ -251,12 +240,12 @@ class CanvasGraph(tk.Canvas):
|
|||
dst.edges.add(edge)
|
||||
self.edges[edge.token] = edge
|
||||
self.core.links[edge.token] = edge
|
||||
if link.HasField("iface1"):
|
||||
if link.iface1:
|
||||
iface1 = link.iface1
|
||||
self.core.iface_to_edge[(node1.id, iface1.id)] = token
|
||||
src.ifaces[iface1.id] = iface1
|
||||
edge.src_iface = iface1
|
||||
if link.HasField("iface2"):
|
||||
if link.iface2:
|
||||
iface2 = link.iface2
|
||||
self.core.iface_to_edge[(node2.id, iface2.id)] = edge.token
|
||||
dst.ifaces[iface2.id] = iface2
|
||||
|
@ -337,19 +326,19 @@ class CanvasGraph(tk.Canvas):
|
|||
self.nodes[node.id] = node
|
||||
self.core.canvas_nodes[core_node.id] = node
|
||||
|
||||
def draw_session(self, session: Session) -> None:
|
||||
def draw_session(self, nodes: List[Node], links: List[Link]) -> None:
|
||||
"""
|
||||
Draw existing session.
|
||||
"""
|
||||
# draw existing nodes
|
||||
for core_node in session.nodes:
|
||||
for core_node in nodes:
|
||||
# peer to peer node is not drawn on the GUI
|
||||
if NodeUtils.is_ignore_node(core_node.type):
|
||||
continue
|
||||
self.add_core_node(core_node)
|
||||
|
||||
# draw existing links
|
||||
for link in session.links:
|
||||
for link in links:
|
||||
logging.debug("drawing link: %s", link)
|
||||
canvas_node1 = self.core.canvas_nodes[link.node1_id]
|
||||
canvas_node2 = self.core.canvas_nodes[link.node2_id]
|
||||
|
@ -987,12 +976,12 @@ class CanvasGraph(tk.Canvas):
|
|||
copy_edge = self.edges[token]
|
||||
copy_link = copy_edge.link
|
||||
options = edge.link.options
|
||||
copy_link.options.CopyFrom(options)
|
||||
copy_link.options = deepcopy(options)
|
||||
iface1_id = None
|
||||
if copy_link.HasField("iface1"):
|
||||
if copy_link.iface1:
|
||||
iface1_id = copy_link.iface1.id
|
||||
iface2_id = None
|
||||
if copy_link.HasField("iface2"):
|
||||
if copy_link.iface2:
|
||||
iface2_id = copy_link.iface2.id
|
||||
if not options.unidirectional:
|
||||
copy_edge.asymmetric_link = None
|
||||
|
|
|
@ -6,8 +6,6 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
|
|||
import grpc
|
||||
from PIL.ImageTk import PhotoImage
|
||||
|
||||
from core.api.grpc.common_pb2 import ConfigOption
|
||||
from core.api.grpc.core_pb2 import Interface, Node, NodeType
|
||||
from core.api.grpc.services_pb2 import NodeServiceData
|
||||
from core.gui import themes
|
||||
from core.gui.dialogs.emaneconfig import EmaneConfigDialog
|
||||
|
@ -22,6 +20,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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
|
|
@ -5,8 +5,8 @@ from typing import Dict, Optional, Tuple
|
|||
from PIL import Image
|
||||
from PIL.ImageTk import PhotoImage
|
||||
|
||||
from core.api.grpc.core_pb2 import NodeType
|
||||
from core.gui.appconfig import LOCAL_ICONS_PATH
|
||||
from core.gui.wrappers import NodeType
|
||||
|
||||
|
||||
class Images:
|
||||
|
|
|
@ -4,9 +4,9 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
|
|||
import netaddr
|
||||
from netaddr import EUI, IPNetwork
|
||||
|
||||
from core.api.grpc.core_pb2 import Interface, Link, Node
|
||||
from core.gui.graph.node import CanvasNode
|
||||
from core.gui.nodeutils import NodeUtils
|
||||
from core.gui.wrappers import Interface, Link, Node
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -89,10 +89,10 @@ class InterfaceManager:
|
|||
remaining_subnets = set()
|
||||
for edge in self.app.core.links.values():
|
||||
link = edge.link
|
||||
if link.HasField("iface1"):
|
||||
if link.iface1:
|
||||
subnets = self.get_subnets(link.iface1)
|
||||
remaining_subnets.add(subnets)
|
||||
if link.HasField("iface2"):
|
||||
if link.iface2:
|
||||
subnets = self.get_subnets(link.iface2)
|
||||
remaining_subnets.add(subnets)
|
||||
|
||||
|
@ -100,9 +100,9 @@ class InterfaceManager:
|
|||
# or remove used indexes from subnet
|
||||
ifaces = []
|
||||
for link in links:
|
||||
if link.HasField("iface1"):
|
||||
if link.iface1:
|
||||
ifaces.append(link.iface1)
|
||||
if link.HasField("iface2"):
|
||||
if link.iface2:
|
||||
ifaces.append(link.iface2)
|
||||
for iface in ifaces:
|
||||
subnets = self.get_subnets(iface)
|
||||
|
@ -117,9 +117,9 @@ class InterfaceManager:
|
|||
def joined(self, links: List[Link]) -> None:
|
||||
ifaces = []
|
||||
for link in links:
|
||||
if link.HasField("iface1"):
|
||||
if link.iface1:
|
||||
ifaces.append(link.iface1)
|
||||
if link.HasField("iface2"):
|
||||
if link.iface2:
|
||||
ifaces.append(link.iface2)
|
||||
|
||||
# add to used subnets and mark used indexes
|
||||
|
|
|
@ -3,9 +3,9 @@ from typing import List, Optional, Set
|
|||
|
||||
from PIL.ImageTk import PhotoImage
|
||||
|
||||
from core.api.grpc.core_pb2 import Node, NodeType
|
||||
from core.gui.appconfig import CustomNode, GuiConfig
|
||||
from core.gui.images import ImageEnum, Images, TypeToImage
|
||||
from core.gui.wrappers import Node, NodeType
|
||||
|
||||
ICON_SIZE: int = 48
|
||||
ANTENNA_SIZE: int = 32
|
||||
|
@ -17,7 +17,7 @@ class NodeDraw:
|
|||
self.image: Optional[PhotoImage] = None
|
||||
self.image_enum: Optional[ImageEnum] = None
|
||||
self.image_file: Optional[str] = None
|
||||
self.node_type: NodeType = None
|
||||
self.node_type: Optional[NodeType] = None
|
||||
self.model: Optional[str] = None
|
||||
self.services: Set[str] = set()
|
||||
self.label: Optional[str] = None
|
||||
|
|
|
@ -5,9 +5,9 @@ import tkinter as tk
|
|||
from tkinter import ttk
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
|
||||
from core.api.grpc.core_pb2 import ExceptionEvent, ExceptionLevel
|
||||
from core.gui.dialogs.alerts import AlertsDialog
|
||||
from core.gui.themes import Styles
|
||||
from core.gui.wrappers import ExceptionEvent, ExceptionLevel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -69,7 +69,7 @@ class StatusBar(ttk.Frame):
|
|||
|
||||
def add_alert(self, event: ExceptionEvent) -> None:
|
||||
self.core_alarms.append(event)
|
||||
level = event.exception_event.level
|
||||
level = event.level
|
||||
self._set_alert_style(level)
|
||||
label = f"Alerts ({len(self.core_alarms)})"
|
||||
self.alerts_button.config(text=label, style=self.alert_style)
|
||||
|
|
|
@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Callable, List, Optional
|
|||
|
||||
from PIL.ImageTk import PhotoImage
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from core.gui.dialogs.colorpicker import ColorPickerDialog
|
||||
from core.gui.dialogs.runtool import RunToolDialog
|
||||
from core.gui.graph import tags
|
||||
|
@ -300,15 +299,15 @@ class Toolbar(ttk.Frame):
|
|||
)
|
||||
task.start()
|
||||
|
||||
def start_callback(self, response: core_pb2.StartSessionResponse) -> None:
|
||||
if response.result:
|
||||
def start_callback(self, result: bool, exceptions: List[str]) -> None:
|
||||
if result:
|
||||
self.set_runtime()
|
||||
self.app.core.set_metadata()
|
||||
self.app.core.show_mobility_players()
|
||||
else:
|
||||
enable_buttons(self.design_frame, enabled=True)
|
||||
if response.exceptions:
|
||||
message = "\n".join(response.exceptions)
|
||||
if exceptions:
|
||||
message = "\n".join(exceptions)
|
||||
self.app.show_error("Start Session Error", message)
|
||||
|
||||
def set_runtime(self) -> None:
|
||||
|
@ -405,7 +404,7 @@ class Toolbar(ttk.Frame):
|
|||
)
|
||||
task.start()
|
||||
|
||||
def stop_callback(self, response: core_pb2.StopSessionResponse) -> None:
|
||||
def stop_callback(self, result: bool) -> None:
|
||||
self.set_design()
|
||||
self.app.canvas.stopped_session()
|
||||
|
||||
|
|
|
@ -5,12 +5,10 @@ from pathlib import Path
|
|||
from tkinter import filedialog, font, ttk
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Set, Type
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from core.api.grpc.common_pb2 import ConfigOption
|
||||
from core.api.grpc.core_pb2 import ConfigOptionType
|
||||
from core.gui import themes, validation
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||
from core.gui.wrappers import ConfigOption, ConfigOptionType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -110,7 +108,7 @@ class ConfigFrame(ttk.Notebook):
|
|||
label = ttk.Label(tab.frame, text=option.label)
|
||||
label.grid(row=index, pady=PADY, padx=PADX, sticky="w")
|
||||
value = tk.StringVar()
|
||||
if option.type == core_pb2.ConfigOptionType.BOOL:
|
||||
if option.type == ConfigOptionType.BOOL:
|
||||
select = ("On", "Off")
|
||||
state = "readonly" if self.enabled else tk.DISABLED
|
||||
combobox = ttk.Combobox(
|
||||
|
@ -129,7 +127,7 @@ class ConfigFrame(ttk.Notebook):
|
|||
tab.frame, textvariable=value, values=select, state=state
|
||||
)
|
||||
combobox.grid(row=index, column=1, sticky="ew")
|
||||
elif option.type == core_pb2.ConfigOptionType.STRING:
|
||||
elif option.type == ConfigOptionType.STRING:
|
||||
value.set(option.value)
|
||||
state = tk.NORMAL if self.enabled else tk.DISABLED
|
||||
if "file" in option.label:
|
||||
|
@ -153,7 +151,7 @@ class ConfigFrame(ttk.Notebook):
|
|||
tab.frame, textvariable=value, state=state
|
||||
)
|
||||
entry.grid(row=index, column=1, sticky="ew")
|
||||
elif option.type == core_pb2.ConfigOptionType.FLOAT:
|
||||
elif option.type == ConfigOptionType.FLOAT:
|
||||
value.set(option.value)
|
||||
state = tk.NORMAL if self.enabled else tk.DISABLED
|
||||
entry = validation.PositiveFloatEntry(
|
||||
|
@ -169,7 +167,7 @@ class ConfigFrame(ttk.Notebook):
|
|||
option = self.config[key]
|
||||
value = self.values[key]
|
||||
config_value = value.get()
|
||||
if option.type == core_pb2.ConfigOptionType.BOOL:
|
||||
if option.type == ConfigOptionType.BOOL:
|
||||
if config_value == "On":
|
||||
option.value = "1"
|
||||
else:
|
||||
|
@ -182,7 +180,7 @@ class ConfigFrame(ttk.Notebook):
|
|||
for name, data in config.items():
|
||||
option = self.config[name]
|
||||
value = self.values[name]
|
||||
if option.type == core_pb2.ConfigOptionType.BOOL:
|
||||
if option.type == ConfigOptionType.BOOL:
|
||||
if data == "1":
|
||||
data = "On"
|
||||
else:
|
||||
|
|
|
@ -1,8 +1,22 @@
|
|||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
from typing import Dict, List
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from core.api.grpc import common_pb2, core_pb2
|
||||
|
||||
|
||||
class ConfigOptionType(Enum):
|
||||
UINT8 = 1
|
||||
UINT16 = 2
|
||||
UINT32 = 3
|
||||
UINT64 = 4
|
||||
INT8 = 5
|
||||
INT16 = 6
|
||||
INT32 = 7
|
||||
INT64 = 8
|
||||
FLOAT = 9
|
||||
STRING = 10
|
||||
BOOL = 11
|
||||
|
||||
|
||||
class SessionState(Enum):
|
||||
|
@ -30,6 +44,292 @@ class NodeType(Enum):
|
|||
LXC = 16
|
||||
|
||||
|
||||
class LinkType(Enum):
|
||||
WIRELESS = 0
|
||||
WIRED = 1
|
||||
|
||||
|
||||
class ExceptionLevel(Enum):
|
||||
DEFAULT = 0
|
||||
FATAL = 1
|
||||
ERROR = 2
|
||||
WARNING = 3
|
||||
NOTICE = 4
|
||||
|
||||
|
||||
class MessageType(Enum):
|
||||
NONE = 0
|
||||
ADD = 1
|
||||
DELETE = 2
|
||||
CRI = 4
|
||||
LOCAL = 8
|
||||
STRING = 16
|
||||
TEXT = 32
|
||||
TTY = 64
|
||||
|
||||
|
||||
@dataclass
|
||||
class SessionLocation:
|
||||
x: float
|
||||
y: float
|
||||
z: float
|
||||
lat: float
|
||||
lon: float
|
||||
alt: float
|
||||
scale: float
|
||||
|
||||
@classmethod
|
||||
def from_proto(cls, location: 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,
|
||||
)
|
||||
|
||||
def to_proto(self) -> core_pb2.SessionLocation:
|
||||
return core_pb2.SessionLocation(
|
||||
x=self.x,
|
||||
y=self.y,
|
||||
z=self.z,
|
||||
lat=self.lat,
|
||||
lon=self.lon,
|
||||
alt=self.alt,
|
||||
scale=self.scale,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExceptionEvent:
|
||||
session_id: int
|
||||
node_id: int
|
||||
level: ExceptionLevel
|
||||
source: str
|
||||
date: str
|
||||
text: str
|
||||
opaque: str
|
||||
|
||||
@classmethod
|
||||
def from_proto(
|
||||
cls, session_id: int, event: 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,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConfigOption:
|
||||
label: str
|
||||
name: str
|
||||
value: str
|
||||
type: ConfigOptionType
|
||||
group: str
|
||||
select: List[str] = None
|
||||
|
||||
@classmethod
|
||||
def from_dict(
|
||||
cls, config: Dict[str, common_pb2.ConfigOption]
|
||||
) -> Dict[str, "ConfigOption"]:
|
||||
d = {}
|
||||
for key, value in config.items():
|
||||
d[key] = ConfigOption.from_proto(value)
|
||||
return d
|
||||
|
||||
@classmethod
|
||||
def to_dict(cls, config: Dict[str, "ConfigOption"]) -> Dict[str, str]:
|
||||
return {k: v.value for k, v in config.items()}
|
||||
|
||||
@classmethod
|
||||
def from_proto(cls, option: 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,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Interface:
|
||||
id: int
|
||||
name: str = None
|
||||
mac: str = None
|
||||
ip4: str = None
|
||||
ip4_mask: int = None
|
||||
ip6: str = None
|
||||
ip6_mask: int = None
|
||||
net_id: int = None
|
||||
flow_id: int = None
|
||||
mtu: int = None
|
||||
node_id: int = None
|
||||
net2_id: int = None
|
||||
|
||||
@classmethod
|
||||
def from_proto(cls, iface: 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,
|
||||
)
|
||||
|
||||
def to_proto(self) -> core_pb2.Interface:
|
||||
return core_pb2.Interface(
|
||||
id=self.id,
|
||||
name=self.name,
|
||||
mac=self.mac,
|
||||
ip4=self.ip4,
|
||||
ip4_mask=self.ip4_mask,
|
||||
ip6=self.ip6,
|
||||
ip6_mask=self.ip6_mask,
|
||||
net_id=self.net_id,
|
||||
flow_id=self.flow_id,
|
||||
mtu=self.mtu,
|
||||
node_id=self.node_id,
|
||||
net2_id=self.net2_id,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class LinkOptions:
|
||||
jitter: int = 0
|
||||
key: int = 0
|
||||
mburst: int = 0
|
||||
mer: int = 0
|
||||
loss: float = 0.0
|
||||
bandwidth: int = 0
|
||||
burst: int = 0
|
||||
delay: int = 0
|
||||
dup: int = 0
|
||||
unidirectional: bool = False
|
||||
|
||||
@classmethod
|
||||
def from_proto(cls, options: 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,
|
||||
)
|
||||
|
||||
def to_proto(self) -> core_pb2.LinkOptions:
|
||||
return core_pb2.LinkOptions(
|
||||
jitter=self.jitter,
|
||||
key=self.key,
|
||||
mburst=self.mburst,
|
||||
mer=self.mer,
|
||||
loss=self.loss,
|
||||
bandwidth=self.bandwidth,
|
||||
burst=self.burst,
|
||||
delay=self.delay,
|
||||
dup=self.dup,
|
||||
unidirectional=self.unidirectional,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Link:
|
||||
node1_id: int
|
||||
node2_id: int
|
||||
type: LinkType = LinkType.WIRED
|
||||
iface1: Interface = None
|
||||
iface2: Interface = None
|
||||
options: LinkOptions = None
|
||||
network_id: int = None
|
||||
label: str = None
|
||||
color: str = None
|
||||
|
||||
@classmethod
|
||||
def from_proto(cls, link: core_pb2.Link) -> "Link":
|
||||
iface1 = None
|
||||
if link.HasField("iface1"):
|
||||
iface1 = Interface.from_proto(link.iface1)
|
||||
iface2 = None
|
||||
if link.HasField("iface2"):
|
||||
iface2 = Interface.from_proto(link.iface2)
|
||||
options = None
|
||||
if link.HasField("options"):
|
||||
options = LinkOptions.from_proto(link.options)
|
||||
return Link(
|
||||
type=LinkType(link.type),
|
||||
node1_id=link.node1_id,
|
||||
node2_id=link.node2_id,
|
||||
iface1=iface1,
|
||||
iface2=iface2,
|
||||
options=options,
|
||||
network_id=link.network_id,
|
||||
label=link.label,
|
||||
color=link.color,
|
||||
)
|
||||
|
||||
def to_proto(self) -> core_pb2.Link:
|
||||
iface1 = self.iface1.to_proto() if self.iface1 else None
|
||||
iface2 = self.iface2.to_proto() if self.iface2 else None
|
||||
options = self.options.to_proto() if self.options else None
|
||||
return core_pb2.Link(
|
||||
type=self.type.value,
|
||||
node1_id=self.node1_id,
|
||||
node2_id=self.node2_id,
|
||||
iface1=iface1,
|
||||
iface2=iface2,
|
||||
options=options,
|
||||
network_id=self.network_id,
|
||||
label=self.label,
|
||||
color=self.color,
|
||||
)
|
||||
|
||||
def is_symmetric(self) -> bool:
|
||||
result = True
|
||||
if self.options:
|
||||
result = self.options.unidirectional is False
|
||||
return result
|
||||
|
||||
|
||||
@dataclass
|
||||
class SessionSummary:
|
||||
id: int
|
||||
state: SessionState
|
||||
nodes: int
|
||||
file: str
|
||||
dir: str
|
||||
|
||||
@classmethod
|
||||
def from_proto(cls, summary: core_pb2.SessionSummary) -> "SessionSummary":
|
||||
return SessionSummary(
|
||||
id=summary.id,
|
||||
state=SessionState(summary.state),
|
||||
nodes=summary.nodes,
|
||||
file=summary.file,
|
||||
dir=summary.dir,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Hook:
|
||||
state: SessionState
|
||||
|
@ -78,8 +378,8 @@ class Node:
|
|||
type: NodeType
|
||||
model: str = None
|
||||
position: Position = None
|
||||
services: List[str] = None
|
||||
config_services: List[str] = None
|
||||
services: List[str] = field(default_factory=list)
|
||||
config_services: List[str] = field(default_factory=list)
|
||||
emane: str = None
|
||||
icon: str = None
|
||||
image: str = None
|
||||
|
@ -120,7 +420,32 @@ class Node:
|
|||
icon=self.icon,
|
||||
image=self.image,
|
||||
server=self.server,
|
||||
geo=self.geo.to_proto(),
|
||||
dir=self.dir,
|
||||
channel=self.channel,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class LinkEvent:
|
||||
message_type: MessageType
|
||||
link: Link
|
||||
|
||||
@classmethod
|
||||
def from_proto(cls, event: core_pb2.LinkEvent) -> "LinkEvent":
|
||||
return LinkEvent(
|
||||
message_type=MessageType(event.message_type),
|
||||
link=Link.from_proto(event.link),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class NodeEvent:
|
||||
message_type: MessageType
|
||||
node: Node
|
||||
|
||||
@classmethod
|
||||
def from_proto(cls, event: core_pb2.NodeEvent) -> "NodeEvent":
|
||||
return NodeEvent(
|
||||
message_type=MessageType(event.message_type),
|
||||
node=Node.from_proto(event.node),
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue