pygui: added type hinting to everything under base core.gui

This commit is contained in:
Blake Harnden 2020-06-19 22:08:24 -07:00
parent adfce52632
commit 0356f3b19c
17 changed files with 473 additions and 430 deletions

View file

@ -4,18 +4,41 @@ Incorporate grpc into python tkinter GUI
import json
import logging
import os
import tkinter as tk
from pathlib import Path
from tkinter import messagebox
from typing import TYPE_CHECKING, Dict, Iterable, List, Optional
from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Set, Tuple
import grpc
from core.api.grpc import client, common_pb2, configservices_pb2, core_pb2
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 (
Event,
ExceptionEvent,
Hook,
Interface,
Link,
LinkEvent,
LinkType,
MessageType,
Node,
NodeEvent,
NodeType,
Position,
SessionLocation,
SessionState,
StartSessionResponse,
StopSessionResponse,
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
from core.api.grpc.wlan_pb2 import WlanConfig
from core.gui import appconfig
from core.gui.appconfig import CoreServer
from core.gui.dialogs.emaneinstall import EmaneInstallDialog
from core.gui.dialogs.error import ErrorDialog
from core.gui.dialogs.mobilityplayer import MobilityPlayer
@ -34,47 +57,46 @@ GUI_SOURCE = "gui"
class CoreClient:
def __init__(self, app: "Application", proxy: bool):
def __init__(self, app: "Application", proxy: bool) -> None:
"""
Create a CoreGrpc instance
"""
self._client = client.CoreGrpcClient(proxy=proxy)
self.session_id = None
self.node_ids = []
self.app = app
self.master = app.master
self.services = {}
self.config_services_groups = {}
self.config_services = {}
self.default_services = {}
self.emane_models = []
self.observer = None
self.app: "Application" = app
self.master: tk.Tk = app.master
self._client: client.CoreGrpcClient = client.CoreGrpcClient(proxy=proxy)
self.session_id: Optional[int] = None
self.services: Dict[str, Set[str]] = {}
self.config_services_groups: Dict[str, Set[str]] = {}
self.config_services: Dict[str, ConfigService] = {}
self.default_services: Dict[NodeType, Set[str]] = {}
self.emane_models: List[str] = []
self.observer: Optional[str] = None
# loaded configuration data
self.servers = {}
self.custom_nodes = {}
self.custom_observers = {}
self.servers: Dict[str, CoreServer] = {}
self.custom_nodes: Dict[str, NodeDraw] = {}
self.custom_observers: Dict[str, str] = {}
self.read_config()
# helpers
self.iface_to_edge = {}
self.ifaces_manager = InterfaceManager(self.app)
self.iface_to_edge: Dict[Tuple[int, int], Tuple[int, int]] = {}
self.ifaces_manager: InterfaceManager = InterfaceManager(self.app)
# session data
self.state = None
self.canvas_nodes = {}
self.location = None
self.links = {}
self.hooks = {}
self.emane_config = None
self.mobility_players = {}
self.handling_throughputs = None
self.handling_events = None
self.xml_dir = None
self.xml_file = None
self.state: Optional[SessionState] = None
self.canvas_nodes: Dict[int, CanvasNode] = {}
self.location: Optional[SessionLocation] = None
self.links: Dict[Tuple[int, int], CanvasEdge] = {}
self.hooks: Dict[str, Hook] = {}
self.emane_config: Dict[str, ConfigOption] = {}
self.mobility_players: Dict[int, MobilityPlayer] = {}
self.handling_throughputs: Optional[grpc.Channel] = None
self.handling_events: Optional[grpc.Channel] = None
self.xml_dir: Optional[str] = None
self.xml_file: Optional[str] = None
@property
def client(self):
def client(self) -> client.CoreGrpcClient:
if self.session_id:
response = self._client.check_session(self.session_id)
if not response.result:
@ -89,7 +111,7 @@ class CoreClient:
self.enable_throughputs()
return self._client
def reset(self):
def reset(self) -> None:
# helpers
self.ifaces_manager.reset()
self.iface_to_edge.clear()
@ -104,14 +126,14 @@ class CoreClient:
self.cancel_throughputs()
self.cancel_events()
def close_mobility_players(self):
def close_mobility_players(self) -> None:
for mobility_player in self.mobility_players.values():
mobility_player.close()
def set_observer(self, value: str):
def set_observer(self, value: Optional[str]) -> None:
self.observer = value
def read_config(self):
def read_config(self) -> None:
# read distributed servers
for server in self.app.guiconfig.servers:
self.servers[server.name] = server
@ -125,7 +147,7 @@ class CoreClient:
for observer in self.app.guiconfig.observers:
self.custom_observers[observer.name] = observer
def handle_events(self, event: core_pb2.Event):
def handle_events(self, event: Event) -> None:
if event.session_id != self.session_id:
logging.warning(
"ignoring event session(%s) current(%s)",
@ -139,7 +161,7 @@ class CoreClient:
elif event.HasField("session_event"):
logging.info("session event: %s", event)
session_event = event.session_event
if session_event.event <= core_pb2.SessionState.SHUTDOWN:
if session_event.event <= SessionState.SHUTDOWN:
self.state = event.session_event.event
elif session_event.event in {7, 8, 9}:
node_id = session_event.node_id
@ -162,7 +184,7 @@ class CoreClient:
else:
logging.info("unhandled event: %s", event)
def handle_link_event(self, event: core_pb2.LinkEvent):
def handle_link_event(self, event: LinkEvent) -> None:
logging.debug("Link event: %s", event)
node1_id = event.link.node1_id
node2_id = event.link.node2_id
@ -171,16 +193,16 @@ class CoreClient:
return
canvas_node1 = self.canvas_nodes[node1_id]
canvas_node2 = self.canvas_nodes[node2_id]
if event.message_type == core_pb2.MessageType.ADD:
if event.message_type == MessageType.ADD:
self.app.canvas.add_wireless_edge(canvas_node1, canvas_node2, event.link)
elif event.message_type == core_pb2.MessageType.DELETE:
elif event.message_type == MessageType.DELETE:
self.app.canvas.delete_wireless_edge(canvas_node1, canvas_node2, event.link)
elif event.message_type == core_pb2.MessageType.NONE:
elif event.message_type == MessageType.NONE:
self.app.canvas.update_wireless_edge(canvas_node1, canvas_node2, event.link)
else:
logging.warning("unknown link event: %s", event)
def handle_node_event(self, event: core_pb2.NodeEvent):
def handle_node_event(self, event: NodeEvent) -> None:
logging.debug("node event: %s", event)
if event.source == GUI_SOURCE:
return
@ -190,22 +212,22 @@ class CoreClient:
canvas_node = self.canvas_nodes[node_id]
canvas_node.move(x, y)
def enable_throughputs(self):
def enable_throughputs(self) -> None:
self.handling_throughputs = self.client.throughputs(
self.session_id, self.handle_throughputs
)
def cancel_throughputs(self):
def cancel_throughputs(self) -> None:
if self.handling_throughputs:
self.handling_throughputs.cancel()
self.handling_throughputs = None
def cancel_events(self):
def cancel_events(self) -> None:
if self.handling_events:
self.handling_events.cancel()
self.handling_events = None
def handle_throughputs(self, event: core_pb2.ThroughputsEvent):
def handle_throughputs(self, event: ThroughputsEvent) -> None:
if event.session_id != self.session_id:
logging.warning(
"ignoring throughput event session(%s) current(%s)",
@ -216,11 +238,11 @@ class CoreClient:
logging.debug("handling throughputs event: %s", event)
self.app.after(0, self.app.canvas.set_throughputs, event)
def handle_exception_event(self, event: core_pb2.ExceptionEvent):
def handle_exception_event(self, event: ExceptionEvent) -> None:
logging.info("exception event: %s", event)
self.app.statusbar.core_alarms.append(event)
def join_session(self, session_id: int, query_location: bool = True):
def join_session(self, session_id: int, query_location: bool = True) -> None:
logging.info("join session(%s)", session_id)
# update session and title
self.session_id = session_id
@ -331,9 +353,9 @@ class CoreClient:
self.app.after(0, self.app.joined_session_update)
def is_runtime(self) -> bool:
return self.state == core_pb2.SessionState.RUNTIME
return self.state == SessionState.RUNTIME
def parse_metadata(self, config: Dict[str, str]):
def parse_metadata(self, config: Dict[str, str]) -> None:
# canvas setting
canvas_config = config.get("canvas")
logging.debug("canvas metadata: %s", canvas_config)
@ -386,7 +408,7 @@ class CoreClient:
except ValueError:
logging.exception("unknown shape: %s", shape_type)
def create_new_session(self):
def create_new_session(self) -> None:
"""
Create a new session
"""
@ -394,7 +416,7 @@ class CoreClient:
response = self.client.create_session()
logging.info("created session: %s", response)
location_config = self.app.guiconfig.location
self.location = core_pb2.SessionLocation(
self.location = SessionLocation(
x=location_config.x,
y=location_config.y,
z=location_config.z,
@ -407,7 +429,7 @@ class CoreClient:
except grpc.RpcError as e:
self.app.show_grpc_exception("New Session Error", e)
def delete_session(self, session_id: int = None):
def delete_session(self, session_id: int = None) -> None:
if session_id is None:
session_id = self.session_id
try:
@ -416,7 +438,7 @@ class CoreClient:
except grpc.RpcError as e:
self.app.show_grpc_exception("Delete Session Error", e)
def setup(self):
def setup(self) -> None:
"""
Query sessions, if there exist any, prompt whether to join one
"""
@ -451,7 +473,7 @@ class CoreClient:
dialog.show()
self.app.close()
def edit_node(self, core_node: core_pb2.Node):
def edit_node(self, core_node: Node) -> None:
try:
self.client.edit_node(
self.session_id, core_node.id, core_node.position, source=GUI_SOURCE
@ -459,12 +481,12 @@ class CoreClient:
except grpc.RpcError as e:
self.app.show_grpc_exception("Edit Node Error", e)
def start_session(self) -> core_pb2.StartSessionResponse:
def start_session(self) -> StartSessionResponse:
self.ifaces_manager.reset_mac()
nodes = [x.core_node for x in self.canvas_nodes.values()]
links = []
for edge in self.links.values():
link = core_pb2.Link()
link = Link()
link.CopyFrom(edge.link)
if link.HasField("iface1") and not link.iface1.mac:
link.iface1.mac = self.ifaces_manager.next_mac()
@ -485,7 +507,7 @@ class CoreClient:
emane_config = {x: self.emane_config[x].value for x in self.emane_config}
else:
emane_config = None
response = core_pb2.StartSessionResponse(result=False)
response = StartSessionResponse(result=False)
try:
response = self.client.start_session(
self.session_id,
@ -511,10 +533,10 @@ class CoreClient:
self.app.show_grpc_exception("Start Session Error", e)
return response
def stop_session(self, session_id: int = None) -> core_pb2.StartSessionResponse:
def stop_session(self, session_id: int = None) -> StopSessionResponse:
if not session_id:
session_id = self.session_id
response = core_pb2.StopSessionResponse(result=False)
response = StopSessionResponse(result=False)
try:
response = self.client.stop_session(session_id)
logging.info("stopped session(%s), result: %s", session_id, response)
@ -522,9 +544,9 @@ class CoreClient:
self.app.show_grpc_exception("Stop Session Error", e)
return response
def show_mobility_players(self):
def show_mobility_players(self) -> None:
for canvas_node in self.canvas_nodes.values():
if canvas_node.core_node.type != core_pb2.NodeType.WIRELESS_LAN:
if canvas_node.core_node.type != NodeType.WIRELESS_LAN:
continue
if canvas_node.mobility_config:
mobility_player = MobilityPlayer(
@ -534,7 +556,7 @@ class CoreClient:
self.mobility_players[node_id] = mobility_player
mobility_player.show()
def set_metadata(self):
def set_metadata(self) -> None:
# create canvas data
wallpaper = None
if self.app.canvas.wallpaper_file:
@ -558,7 +580,7 @@ class CoreClient:
response = self.client.set_session_metadata(self.session_id, metadata)
logging.info("set session metadata %s, result: %s", metadata, response)
def launch_terminal(self, node_id: int):
def launch_terminal(self, node_id: int) -> None:
try:
terminal = self.app.guiconfig.preferences.terminal
if not terminal:
@ -575,12 +597,12 @@ class CoreClient:
except grpc.RpcError as e:
self.app.show_grpc_exception("Node Terminal Error", e)
def save_xml(self, file_path: str):
def save_xml(self, file_path: str) -> None:
"""
Save core session as to an xml file
"""
try:
if self.state != core_pb2.SessionState.RUNTIME:
if self.state != SessionState.RUNTIME:
logging.debug("Send session data to the daemon")
self.send_data()
response = self.client.save_xml(self.session_id, file_path)
@ -588,7 +610,7 @@ class CoreClient:
except grpc.RpcError as e:
self.app.show_grpc_exception("Save XML Error", e)
def open_xml(self, file_path: str):
def open_xml(self, file_path: str) -> None:
"""
Open core xml
"""
@ -627,7 +649,8 @@ class CoreClient:
shutdown=shutdowns,
)
logging.info(
"Set %s service for node(%s), files: %s, Startup: %s, Validation: %s, Shutdown: %s, Result: %s",
"Set %s service for node(%s), files: %s, Startup: %s, "
"Validation: %s, Shutdown: %s, Result: %s",
service_name,
node_id,
files,
@ -656,7 +679,7 @@ class CoreClient:
def set_node_service_file(
self, node_id: int, service_name: str, file_name: str, data: str
):
) -> None:
response = self.client.set_node_service_file(
self.session_id, node_id, service_name, file_name, data
)
@ -669,18 +692,16 @@ class CoreClient:
response,
)
def create_nodes_and_links(self):
def create_nodes_and_links(self) -> None:
"""
create nodes and links that have not been created yet
"""
node_protos = [x.core_node for x in self.canvas_nodes.values()]
link_protos = [x.link for x in self.links.values()]
if self.state != core_pb2.SessionState.DEFINITION:
self.client.set_session_state(
self.session_id, core_pb2.SessionState.DEFINITION
)
if self.state != SessionState.DEFINITION:
self.client.set_session_state(self.session_id, SessionState.DEFINITION)
self.client.set_session_state(self.session_id, core_pb2.SessionState.DEFINITION)
self.client.set_session_state(self.session_id, SessionState.DEFINITION)
for node_proto in node_protos:
response = self.client.add_node(self.session_id, node_proto)
logging.debug("create node: %s", response)
@ -695,7 +716,7 @@ class CoreClient:
)
logging.debug("create link: %s", response)
def send_data(self):
def send_data(self) -> None:
"""
send to daemon all session info, but don't start the session
"""
@ -738,10 +759,9 @@ class CoreClient:
if self.emane_config:
config = {x: self.emane_config[x].value for x in self.emane_config}
self.client.set_emane_config(self.session_id, config)
self.set_metadata()
def close(self):
def close(self) -> None:
"""
Clean ups when done using grpc
"""
@ -760,31 +780,31 @@ class CoreClient:
return i
def create_node(
self, x: float, y: float, node_type: core_pb2.NodeType, model: str
) -> Optional[core_pb2.Node]:
self, x: float, y: float, node_type: NodeType, model: str
) -> Optional[Node]:
"""
Add node, with information filled in, to grpc manager
"""
node_id = self.next_node_id()
position = core_pb2.Position(x=x, y=y)
position = Position(x=x, y=y)
image = None
if NodeUtils.is_image_node(node_type):
image = "ubuntu:latest"
emane = None
if node_type == core_pb2.NodeType.EMANE:
if node_type == NodeType.EMANE:
if not self.emane_models:
dialog = EmaneInstallDialog(self.app)
dialog.show()
return
emane = self.emane_models[0]
name = f"EMANE{node_id}"
elif node_type == core_pb2.NodeType.WIRELESS_LAN:
elif node_type == NodeType.WIRELESS_LAN:
name = f"WLAN{node_id}"
elif node_type in [core_pb2.NodeType.RJ45, core_pb2.NodeType.TUNNEL]:
elif node_type in [NodeType.RJ45, NodeType.TUNNEL]:
name = "UNASSIGNED"
else:
name = f"n{node_id}"
node = core_pb2.Node(
node = Node(
id=node_id,
type=node_type,
name=name,
@ -810,7 +830,7 @@ class CoreClient:
)
return node
def deleted_graph_nodes(self, canvas_nodes: List[core_pb2.Node]):
def deleted_graph_nodes(self, canvas_nodes: List[Node]) -> None:
"""
remove the nodes selected by the user and anything related to that node
such as link, configurations, interfaces
@ -826,14 +846,14 @@ class CoreClient:
links.append(edge.link)
self.ifaces_manager.removed(links)
def create_iface(self, canvas_node: CanvasNode) -> core_pb2.Interface:
def create_iface(self, canvas_node: CanvasNode) -> Interface:
node = canvas_node.core_node
ip4, ip6 = self.ifaces_manager.get_ips(node)
ip4_mask = self.ifaces_manager.ip4_mask
ip6_mask = self.ifaces_manager.ip6_mask
iface_id = canvas_node.next_iface_id()
name = f"eth{iface_id}"
iface = core_pb2.Interface(
iface = Interface(
id=iface_id,
name=name,
ip4=ip4,
@ -852,7 +872,7 @@ class CoreClient:
def create_link(
self, edge: CanvasEdge, canvas_src_node: CanvasNode, canvas_dst_node: CanvasNode
):
) -> None:
"""
Create core link for a pair of canvas nodes, with token referencing
the canvas edge.
@ -873,8 +893,8 @@ class CoreClient:
dst_iface = self.create_iface(canvas_dst_node)
self.iface_to_edge[(dst_node.id, dst_iface.id)] = edge.token
link = core_pb2.Link(
type=core_pb2.LinkType.WIRED,
link = Link(
type=LinkType.WIRED,
node1_id=src_node.id,
node2_id=dst_node.id,
iface1=src_iface,
@ -896,7 +916,7 @@ class CoreClient:
def get_wlan_configs_proto(self) -> List[WlanConfig]:
configs = []
for canvas_node in self.canvas_nodes.values():
if canvas_node.core_node.type != core_pb2.NodeType.WIRELESS_LAN:
if canvas_node.core_node.type != NodeType.WIRELESS_LAN:
continue
if not canvas_node.wlan_config:
continue
@ -910,7 +930,7 @@ class CoreClient:
def get_mobility_configs_proto(self) -> List[MobilityConfig]:
configs = []
for canvas_node in self.canvas_nodes.values():
if canvas_node.core_node.type != core_pb2.NodeType.WIRELESS_LAN:
if canvas_node.core_node.type != NodeType.WIRELESS_LAN:
continue
if not canvas_node.mobility_config:
continue
@ -924,7 +944,7 @@ class CoreClient:
def get_emane_model_configs_proto(self) -> List[EmaneModelConfig]:
configs = []
for canvas_node in self.canvas_nodes.values():
if canvas_node.core_node.type != core_pb2.NodeType.EMANE:
if canvas_node.core_node.type != NodeType.EMANE:
continue
node_id = canvas_node.core_node.id
for key, config in canvas_node.emane_model_configs.items():
@ -975,9 +995,7 @@ class CoreClient:
configs.append(config_proto)
return configs
def get_config_service_configs_proto(
self
) -> List[configservices_pb2.ConfigServiceConfig]:
def get_config_service_configs_proto(self) -> List[ConfigServiceConfig]:
config_service_protos = []
for canvas_node in self.canvas_nodes.values():
if not NodeUtils.is_container_node(canvas_node.core_node.type):
@ -987,7 +1005,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 = configservices_pb2.ConfigServiceConfig(
config_proto = ConfigServiceConfig(
node_id=node_id,
name=name,
templates=service_config["templates"],
@ -1000,7 +1018,7 @@ class CoreClient:
logging.info("running node(%s) cmd: %s", node_id, self.observer)
return self.client.node_command(self.session_id, node_id, self.observer).output
def get_wlan_config(self, node_id: int) -> Dict[str, common_pb2.ConfigOption]:
def get_wlan_config(self, node_id: int) -> Dict[str, ConfigOption]:
response = self.client.get_wlan_config(self.session_id, node_id)
config = response.config
logging.debug(
@ -1010,7 +1028,7 @@ class CoreClient:
)
return dict(config)
def get_mobility_config(self, node_id: int) -> Dict[str, common_pb2.ConfigOption]:
def get_mobility_config(self, node_id: int) -> Dict[str, ConfigOption]:
response = self.client.get_mobility_config(self.session_id, node_id)
config = response.config
logging.debug(
@ -1022,7 +1040,7 @@ class CoreClient:
def get_emane_model_config(
self, node_id: int, model: str, iface_id: int = None
) -> Dict[str, common_pb2.ConfigOption]:
) -> Dict[str, ConfigOption]:
if iface_id is None:
iface_id = -1
response = self.client.get_emane_model_config(
@ -1030,7 +1048,8 @@ class CoreClient:
)
config = response.config
logging.debug(
"get emane model config: node id: %s, EMANE model: %s, interface: %s, config: %s",
"get emane model config: node id: %s, EMANE model: %s, "
"interface: %s, config: %s",
node_id,
model,
iface_id,
@ -1038,7 +1057,7 @@ class CoreClient:
)
return dict(config)
def execute_script(self, script):
def execute_script(self, script) -> None:
response = self.client.execute_script(script)
logging.info("execute python script %s", response)
if response.session_id != -1: