pygui: changes to make use of wrapped session object and wrapped nodes to maintain and retrieving configurations information

This commit is contained in:
Blake Harnden 2020-07-28 00:03:15 -07:00
parent 3bdd6292cd
commit 588afaad13
21 changed files with 284 additions and 455 deletions

View file

@ -580,6 +580,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
service_configs = grpcutils.get_node_service_configs(session)
config_service_configs = grpcutils.get_node_config_service_configs(session)
session_proto = core_pb2.Session(
id=session.id,
state=session.state.value,
nodes=nodes,
links=links,

View file

@ -37,7 +37,6 @@ from core.gui.wrappers import (
ConfigOption,
ConfigService,
ExceptionEvent,
Hook,
Interface,
Link,
LinkEvent,
@ -61,6 +60,10 @@ GUI_SOURCE = "gui"
CPU_USAGE_DELAY = 3
def to_dict(config: Dict[str, ConfigOption]) -> Dict[str, str]:
return {x: y.value for x, y in config.items()}
class CoreClient:
def __init__(self, app: "Application", proxy: bool) -> None:
"""
@ -69,14 +72,13 @@ class CoreClient:
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.session: Optional[Session] = None
self.user = getpass.getuser()
# global service settings
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
self.user = getpass.getuser()
# loaded configuration data
self.servers: Dict[str, CoreServer] = {}
@ -87,15 +89,12 @@ class CoreClient:
# helpers
self.iface_to_edge: Dict[Tuple[int, ...], Tuple[int, ...]] = {}
self.ifaces_manager: InterfaceManager = InterfaceManager(self.app)
self.observer: Optional[str] = None
# session data
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.canvas_nodes: Dict[int, CanvasNode] = {}
self.links: Dict[Tuple[int, int], CanvasEdge] = {}
self.handling_throughputs: Optional[grpc.Future] = None
self.handling_cpu_usage: Optional[grpc.Future] = None
self.handling_events: Optional[grpc.Future] = None
@ -104,15 +103,15 @@ class CoreClient:
@property
def client(self) -> client.CoreGrpcClient:
if self.session_id:
response = self._client.check_session(self.session_id)
if self.session:
response = self._client.check_session(self.session.id)
if not response.result:
throughputs_enabled = self.handling_throughputs is not None
self.cancel_throughputs()
self.cancel_events()
self._client.create_session(self.session_id)
self._client.create_session(self.session.id)
self.handling_events = self._client.events(
self.session_id, self.handle_events
self.session.id, self.handle_events
)
if throughputs_enabled:
self.enable_throughputs()
@ -126,8 +125,6 @@ class CoreClient:
# session data
self.canvas_nodes.clear()
self.links.clear()
self.hooks.clear()
self.emane_config = None
self.close_mobility_players()
self.mobility_players.clear()
# clear streams
@ -145,12 +142,10 @@ class CoreClient:
# read distributed servers
for server in self.app.guiconfig.servers:
self.servers[server.name] = server
# read custom nodes
for custom_node in self.app.guiconfig.nodes:
node_draw = NodeDraw.from_custom(custom_node)
self.custom_nodes[custom_node.name] = node_draw
# read observers
for observer in self.app.guiconfig.observers:
self.custom_observers[observer.name] = observer
@ -158,11 +153,11 @@ class CoreClient:
def handle_events(self, event: core_pb2.Event) -> None:
if event.source == GUI_SOURCE:
return
if event.session_id != self.session_id:
if event.session_id != self.session.id:
logging.warning(
"ignoring event session(%s) current(%s)",
event.session_id,
self.session_id,
self.session.id,
)
return
@ -173,7 +168,7 @@ class CoreClient:
logging.info("session event: %s", event)
session_event = event.session_event
if session_event.event <= SessionState.SHUTDOWN.value:
self.state = SessionState(session_event.event)
self.session.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)
@ -253,7 +248,7 @@ class CoreClient:
def enable_throughputs(self) -> None:
self.handling_throughputs = self.client.throughputs(
self.session_id, self.handle_throughputs
self.session.id, self.handle_throughputs
)
def cancel_throughputs(self) -> None:
@ -283,11 +278,11 @@ class CoreClient:
def handle_throughputs(self, event: core_pb2.ThroughputsEvent) -> None:
event = ThroughputsEvent.from_proto(event)
if event.session_id != self.session_id:
if event.session_id != self.session.id:
logging.warning(
"ignoring throughput event session(%s) current(%s)",
event.session_id,
self.session_id,
self.session.id,
)
return
logging.debug("handling throughputs event: %s", event)
@ -300,126 +295,33 @@ class CoreClient:
logging.info("exception event: %s", event)
self.app.statusbar.add_alert(event)
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
self.master.title(f"CORE Session({self.session_id})")
# clear session data
def join_session(self, session_id: int) -> None:
logging.info("joining session(%s)", session_id)
self.reset()
# get session data
try:
response = self.client.get_session(self.session_id)
session = Session.from_proto(response.session)
self.state = session.state
response = self.client.get_session(session_id)
self.session = Session.from_proto(response.session)
self.client.set_session_user(self.session.id, self.user)
self.master.title(f"CORE Session({self.session.id})")
self.handling_events = self.client.events(
self.session_id, self.handle_events
self.session.id, self.handle_events
)
# set session user
self.client.set_session_user(self.session_id, self.user)
# get session service defaults
response = self.client.get_service_defaults(self.session_id)
self.default_services = {
x.node_type: set(x.services) for x in response.defaults
}
# get location
if query_location:
response = self.client.get_session_location(self.session_id)
self.location = SessionLocation.from_proto(response.location)
# get emane models
response = self.client.get_emane_models(self.session_id)
self.emane_models = response.models
# get hooks
response = self.client.get_hooks(self.session_id)
for hook_proto in response.hooks:
hook = Hook.from_proto(hook_proto)
self.hooks[hook.file] = hook
# get emane config
response = self.client.get_emane_config(self.session_id)
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)
# 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 = ConfigOption.from_dict(config)
# get emane model config
response = self.client.get_emane_model_configs(self.session_id)
for config in response.configs:
iface_id = None
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)
] = 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 = ConfigOption.from_dict(mapped_config.config)
# get service configurations
response = self.client.get_node_service_configs(self.session_id)
for config in response.configs:
canvas_node = self.canvas_nodes[config.node_id]
canvas_node.service_configs[config.service] = config.data
logging.debug("service file configs: %s", config.files)
for file_name in config.files:
data = config.files[file_name]
files = canvas_node.service_file_configs.setdefault(
config.service, {}
)
files[file_name] = data
# get config service configurations
response = self.client.get_node_config_service_configs(self.session_id)
for config in response.configs:
canvas_node = self.canvas_nodes[config.node_id]
service_config = canvas_node.config_service_configs.setdefault(
config.name, {}
)
if config.templates:
service_config["templates"] = config.templates
if config.config:
service_config["config"] = config.config
# get metadata
response = self.client.get_session_metadata(self.session_id)
self.parse_metadata(response.config)
self.ifaces_manager.joined(self.session.links)
self.app.canvas.reset_and_redraw(self.session)
self.parse_metadata()
self.app.canvas.organize()
if self.is_runtime():
self.show_mobility_players()
self.app.after(0, self.app.joined_session_update)
except grpc.RpcError as e:
self.app.show_grpc_exception("Join Session Error", e)
# organize canvas
self.app.canvas.organize()
if self.is_runtime():
self.show_mobility_players()
# update ui to represent current state
self.app.after(0, self.app.joined_session_update)
def is_runtime(self) -> bool:
return self.state == SessionState.RUNTIME
return self.session and self.session.state == SessionState.RUNTIME
def parse_metadata(self, config: Dict[str, str]) -> None:
def parse_metadata(self) -> None:
# canvas setting
config = self.session.metadata
canvas_config = config.get("canvas")
logging.debug("canvas metadata: %s", canvas_config)
if canvas_config:
@ -447,7 +349,7 @@ class CoreClient:
if shapes_config:
shapes_config = json.loads(shapes_config)
for shape_config in shapes_config:
logging.info("loading shape: %s", shape_config)
logging.debug("loading shape: %s", shape_config)
shape_type = shape_config["type"]
try:
shape_type = ShapeType(shape_type)
@ -478,8 +380,9 @@ class CoreClient:
try:
response = self.client.create_session()
logging.info("created session: %s", response)
self.join_session(response.session_id)
location_config = self.app.guiconfig.location
self.location = SessionLocation(
self.session.location = SessionLocation(
x=location_config.x,
y=location_config.y,
z=location_config.z,
@ -488,13 +391,12 @@ class CoreClient:
alt=location_config.alt,
scale=location_config.scale,
)
self.join_session(response.session_id, query_location=False)
except grpc.RpcError as e:
self.app.show_grpc_exception("New Session Error", e)
def delete_session(self, session_id: int = None) -> None:
if session_id is None:
session_id = self.session_id
session_id = self.session.id
try:
response = self.client.delete_session(session_id)
logging.info("deleted session(%s), Result: %s", session_id, response)
@ -507,13 +409,11 @@ class CoreClient:
"""
try:
self.client.connect()
# get service information
# get all available services
response = self.client.get_services()
for service in response.services:
group_services = self.services.setdefault(service.group, set())
group_services.add(service.name)
# get config service informations
response = self.client.get_config_services()
for service in response.services:
@ -522,7 +422,6 @@ class CoreClient:
service.group, set()
)
group_services.add(service.name)
# join provided session, create new session, or show dialog to select an
# existing session
response = self.client.get_sessions()
@ -553,14 +452,14 @@ class CoreClient:
try:
position = core_node.position.to_proto()
self.client.edit_node(
self.session_id, core_node.id, 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)
def send_servers(self) -> None:
for server in self.servers.values():
self.client.add_session_server(self.session_id, server.name, server.address)
self.client.add_session_server(self.session.id, server.name, server.address)
def start_session(self) -> Tuple[bool, List[str]]:
self.ifaces_manager.reset_mac()
@ -576,26 +475,23 @@ class CoreClient:
wlan_configs = self.get_wlan_configs_proto()
mobility_configs = self.get_mobility_configs_proto()
emane_model_configs = self.get_emane_model_configs_proto()
hooks = [x.to_proto() for x in self.hooks.values()]
hooks = [x.to_proto() for x in self.session.hooks.values()]
service_configs = self.get_service_configs_proto()
file_configs = self.get_service_file_configs_proto()
asymmetric_links = [
x.asymmetric_link for x in self.links.values() if x.asymmetric_link
]
config_service_configs = self.get_config_service_configs_proto()
if self.emane_config:
emane_config = {x: self.emane_config[x].value for x in self.emane_config}
else:
emane_config = None
emane_config = to_dict(self.session.emane_config)
result = False
exceptions = []
try:
self.send_servers()
response = self.client.start_session(
self.session_id,
self.session.id,
nodes,
links,
self.location.to_proto(),
self.session.location.to_proto(),
hooks,
emane_config,
emane_model_configs,
@ -607,7 +503,7 @@ class CoreClient:
config_service_configs,
)
logging.info(
"start session(%s), result: %s", self.session_id, response.result
"start session(%s), result: %s", self.session.id, response.result
)
if response.result:
self.set_metadata()
@ -619,7 +515,7 @@ class CoreClient:
def stop_session(self, session_id: int = None) -> bool:
if not session_id:
session_id = self.session_id
session_id = self.session.id
result = False
try:
response = self.client.stop_session(session_id)
@ -630,15 +526,12 @@ class CoreClient:
return result
def show_mobility_players(self) -> None:
for canvas_node in self.canvas_nodes.values():
if canvas_node.core_node.type != NodeType.WIRELESS_LAN:
for node in self.session.nodes.values():
if node.type != NodeType.WIRELESS_LAN:
continue
if canvas_node.mobility_config:
mobility_player = MobilityPlayer(
self.app, canvas_node, canvas_node.mobility_config
)
node_id = canvas_node.core_node.id
self.mobility_players[node_id] = mobility_player
if node.mobility_config:
mobility_player = MobilityPlayer(self.app, node)
self.mobility_players[node.id] = mobility_player
mobility_player.show()
def set_metadata(self) -> None:
@ -662,8 +555,8 @@ class CoreClient:
shapes = json.dumps(shapes)
metadata = {"canvas": canvas_config, "shapes": shapes}
response = self.client.set_session_metadata(self.session_id, metadata)
logging.info("set session metadata %s, result: %s", metadata, response)
response = self.client.set_session_metadata(self.session.id, metadata)
logging.debug("set session metadata %s, result: %s", metadata, response)
def launch_terminal(self, node_id: int) -> None:
try:
@ -675,7 +568,7 @@ class CoreClient:
parent=self.app,
)
return
response = self.client.get_node_terminal(self.session_id, node_id)
response = self.client.get_node_terminal(self.session.id, node_id)
cmd = f"{terminal} {response.terminal} &"
logging.info("launching terminal %s", cmd)
os.system(cmd)
@ -687,10 +580,10 @@ class CoreClient:
Save core session as to an xml file
"""
try:
if self.state != SessionState.RUNTIME:
if not self.is_runtime():
logging.debug("Send session data to the daemon")
self.send_data()
response = self.client.save_xml(self.session_id, file_path)
response = self.client.save_xml(self.session.id, file_path)
logging.info("saved xml file %s, result: %s", file_path, response)
except grpc.RpcError as e:
self.app.show_grpc_exception("Save XML Error", e)
@ -707,7 +600,7 @@ class CoreClient:
self.app.show_grpc_exception("Open XML Error", e)
def get_node_service(self, node_id: int, service_name: str) -> NodeServiceData:
response = self.client.get_node_service(self.session_id, node_id, service_name)
response = self.client.get_node_service(self.session.id, node_id, service_name)
logging.debug(
"get node(%s) %s service, response: %s", node_id, service_name, response
)
@ -724,7 +617,7 @@ class CoreClient:
shutdowns: List[str],
) -> NodeServiceData:
response = self.client.set_node_service(
self.session_id,
self.session.id,
node_id,
service_name,
directories=dirs,
@ -744,14 +637,14 @@ class CoreClient:
shutdowns,
response,
)
response = self.client.get_node_service(self.session_id, node_id, service_name)
response = self.client.get_node_service(self.session.id, node_id, service_name)
return NodeServiceData.from_proto(response.service)
def get_node_service_file(
self, node_id: int, service_name: str, file_name: str
) -> str:
response = self.client.get_node_service_file(
self.session_id, node_id, service_name, file_name
self.session.id, node_id, service_name, file_name
)
logging.debug(
"get service file for node(%s), service: %s, file: %s, result: %s",
@ -766,7 +659,7 @@ class CoreClient:
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
self.session.id, node_id, service_name, file_name, data
)
logging.info(
"set node(%s) service file, service: %s, file: %s, data: %s, result: %s",
@ -783,13 +676,13 @@ class CoreClient:
"""
node_protos = [x.core_node.to_proto() for x in self.canvas_nodes.values()]
link_protos = [x.link.to_proto() for x in self.links.values()]
self.client.set_session_state(self.session_id, SessionState.DEFINITION.value)
self.client.set_session_state(self.session.id, SessionState.DEFINITION.value)
for node_proto in node_protos:
response = self.client.add_node(self.session_id, node_proto)
response = self.client.add_node(self.session.id, node_proto)
logging.debug("create node: %s", response)
for link_proto in link_protos:
response = self.client.add_link(
self.session_id,
self.session.id,
link_proto.node1_id,
link_proto.node2_id,
link_proto.iface1,
@ -806,15 +699,15 @@ class CoreClient:
self.create_nodes_and_links()
for config_proto in self.get_wlan_configs_proto():
self.client.set_wlan_config(
self.session_id, config_proto.node_id, config_proto.config
self.session.id, config_proto.node_id, config_proto.config
)
for config_proto in self.get_mobility_configs_proto():
self.client.set_mobility_config(
self.session_id, config_proto.node_id, config_proto.config
self.session.id, config_proto.node_id, config_proto.config
)
for config_proto in self.get_service_configs_proto():
self.client.set_node_service(
self.session_id,
self.session.id,
config_proto.node_id,
config_proto.service,
startup=config_proto.startup,
@ -823,38 +716,37 @@ class CoreClient:
)
for config_proto in self.get_service_file_configs_proto():
self.client.set_node_service_file(
self.session_id,
self.session.id,
config_proto.node_id,
config_proto.service,
config_proto.file,
config_proto.data,
)
for hook in self.hooks.values():
for hook in self.session.hooks.values():
self.client.add_hook(
self.session_id, hook.state.value, hook.file, hook.data
self.session.id, hook.state.value, hook.file, hook.data
)
for config_proto in self.get_emane_model_configs_proto():
self.client.set_emane_model_config(
self.session_id,
self.session.id,
config_proto.node_id,
config_proto.model,
config_proto.config,
config_proto.iface_id,
)
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)
if self.location:
self.client.set_session_location(
self.session_id,
self.location.x,
self.location.y,
self.location.z,
self.location.lat,
self.location.lon,
self.location.alt,
self.location.scale,
)
config = to_dict(self.session.emane_config)
self.client.set_emane_config(self.session.id, config)
location = self.session.location
self.client.set_session_location(
self.session.id,
location.x,
location.y,
location.z,
location.lat,
location.lon,
location.alt,
location.scale,
)
self.set_metadata()
def close(self) -> None:
@ -888,16 +780,16 @@ class CoreClient:
image = "ubuntu:latest"
emane = None
if node_type == NodeType.EMANE:
if not self.emane_models:
if not self.session.emane_models:
dialog = EmaneInstallDialog(self.app)
dialog.show()
return
emane = self.emane_models[0]
name = f"EMANE{node_id}"
emane = self.session.emane_models[0]
name = f"emane{node_id}"
elif node_type == NodeType.WIRELESS_LAN:
name = f"WLAN{node_id}"
name = f"wlan{node_id}"
elif node_type in [NodeType.RJ45, NodeType.TUNNEL]:
name = "UNASSIGNED"
name = "unassigned"
else:
name = f"n{node_id}"
node = Node(
@ -914,13 +806,13 @@ class CoreClient:
node.services[:] = services
# assign default services to CORE node
else:
services = self.default_services.get(model)
services = self.session.default_services.get(model)
if services:
node.services[:] = services
node.services = services.copy()
logging.info(
"add node(%s) to session(%s), coordinates(%s, %s)",
node.name,
self.session_id,
self.session.id,
x,
y,
)
@ -1005,60 +897,56 @@ class CoreClient:
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:
for node in self.session.nodes.values():
if node.type != NodeType.WIRELESS_LAN:
continue
if not canvas_node.wlan_config:
if not node.wlan_config:
continue
config = ConfigOption.to_dict(canvas_node.wlan_config)
node_id = canvas_node.core_node.id
wlan_config = wlan_pb2.WlanConfig(node_id=node_id, config=config)
config = ConfigOption.to_dict(node.wlan_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[mobility_pb2.MobilityConfig]:
configs = []
for canvas_node in self.canvas_nodes.values():
if canvas_node.core_node.type != NodeType.WIRELESS_LAN:
for node in self.session.nodes.values():
if node.type != NodeType.WIRELESS_LAN:
continue
if not canvas_node.mobility_config:
if not node.mobility_config:
continue
config = ConfigOption.to_dict(canvas_node.mobility_config)
node_id = canvas_node.core_node.id
config = ConfigOption.to_dict(node.mobility_config)
mobility_config = mobility_pb2.MobilityConfig(
node_id=node_id, config=config
node_id=node.id, config=config
)
configs.append(mobility_config)
return configs
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:
for node in self.session.nodes.values():
if node.type != NodeType.EMANE:
continue
node_id = canvas_node.core_node.id
for key, config in canvas_node.emane_model_configs.items():
for key, config in node.emane_model_configs.items():
model, iface_id = key
config = ConfigOption.to_dict(config)
if iface_id is None:
iface_id = -1
config_proto = emane_pb2.EmaneModelConfig(
node_id=node_id, iface_id=iface_id, model=model, config=config
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[services_pb2.ServiceConfig]:
configs = []
for canvas_node in self.canvas_nodes.values():
if not NodeUtils.is_container_node(canvas_node.core_node.type):
for node in self.session.nodes.values():
if not NodeUtils.is_container_node(node.type):
continue
if not canvas_node.service_configs:
if not node.service_configs:
continue
node_id = canvas_node.core_node.id
for name, config in canvas_node.service_configs.items():
for name, config in node.service_configs.items():
config_proto = services_pb2.ServiceConfig(
node_id=node_id,
node_id=node.id,
service=name,
directories=config.dirs,
files=config.configs,
@ -1071,16 +959,15 @@ class CoreClient:
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):
for node in self.session.nodes.values():
if not NodeUtils.is_container_node(node.type):
continue
if not canvas_node.service_file_configs:
if not node.service_file_configs:
continue
node_id = canvas_node.core_node.id
for service, file_configs in canvas_node.service_file_configs.items():
for service, file_configs in node.service_file_configs.items():
for file, data in file_configs.items():
config_proto = services_pb2.ServiceFileConfig(
node_id=node_id, service=service, file=file, data=data
node_id=node.id, service=service, file=file, data=data
)
configs.append(config_proto)
return configs
@ -1089,29 +976,27 @@ class CoreClient:
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):
for node in self.session.nodes.values():
if not NodeUtils.is_container_node(node.type):
continue
if not canvas_node.config_service_configs:
if not node.config_service_configs:
continue
node_id = canvas_node.core_node.id
for name, service_config in canvas_node.config_service_configs.items():
config = service_config.get("config", {})
for name, service_config in node.config_service_configs.items():
config_proto = configservices_pb2.ConfigServiceConfig(
node_id=node_id,
node_id=node.id,
name=name,
templates=service_config["templates"],
config=config,
templates=service_config.templates,
config=service_config.config,
)
config_service_protos.append(config_proto)
return config_service_protos
def run(self, node_id: int) -> str:
logging.info("running node(%s) cmd: %s", node_id, self.observer)
return self.client.node_command(self.session_id, node_id, self.observer).output
return self.client.node_command(self.session.id, node_id, self.observer).output
def get_wlan_config(self, node_id: int) -> Dict[str, ConfigOption]:
response = self.client.get_wlan_config(self.session_id, node_id)
response = self.client.get_wlan_config(self.session.id, node_id)
config = response.config
logging.debug(
"get wlan configuration from node %s, result configuration: %s",
@ -1121,7 +1006,7 @@ class CoreClient:
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)
response = self.client.get_mobility_config(self.session.id, node_id)
config = response.config
logging.debug(
"get mobility config from node %s, result configuration: %s",
@ -1136,7 +1021,7 @@ class CoreClient:
if iface_id is None:
iface_id = -1
response = self.client.get_emane_model_config(
self.session_id, node_id, model, iface_id
self.session.id, node_id, model, iface_id
)
config = response.config
logging.debug(

View file

@ -27,7 +27,7 @@ class SizeAndScaleDialog(Dialog):
width, height = self.canvas.current_dimensions
self.pixel_width: tk.IntVar = tk.IntVar(value=width)
self.pixel_height: tk.IntVar = tk.IntVar(value=height)
location = self.app.core.location
location = self.app.core.session.location
self.x: tk.DoubleVar = tk.DoubleVar(value=location.x)
self.y: tk.DoubleVar = tk.DoubleVar(value=location.y)
self.lat: tk.DoubleVar = tk.DoubleVar(value=location.lat)
@ -192,7 +192,7 @@ class SizeAndScaleDialog(Dialog):
self.canvas.redraw_canvas((width, height))
if self.canvas.wallpaper:
self.canvas.redraw_wallpaper()
location = self.app.core.location
location = self.app.core.session.location
location.x = self.x.get()
location.y = self.y.get()
location.lat = self.lat.get()

View file

@ -11,28 +11,26 @@ import grpc
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, ServiceValidationMode
from core.gui.wrappers import (
ConfigOption,
ConfigServiceData,
Node,
ServiceValidationMode,
)
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
from core.gui.coreclient import CoreClient
class ConfigServiceConfigDialog(Dialog):
def __init__(
self,
master: tk.BaseWidget,
app: "Application",
service_name: str,
canvas_node: "CanvasNode",
node_id: int,
self, master: tk.BaseWidget, app: "Application", service_name: str, node: Node
) -> None:
title = f"{service_name} Config Service"
super().__init__(app, title, master=master)
self.core: "CoreClient" = app.core
self.canvas_node: "CanvasNode" = canvas_node
self.node_id: int = node_id
self.node: Node = node
self.service_name: str = service_name
self.radiovar: tk.IntVar = tk.IntVar()
self.radiovar.set(2)
@ -50,7 +48,7 @@ class ConfigServiceConfigDialog(Dialog):
self.validation_time: Optional[int] = None
self.validation_period: tk.StringVar = tk.StringVar()
self.modes: List[str] = []
self.mode_configs: Dict[str, str] = {}
self.mode_configs: Dict[str, Dict[str, str]] = {}
self.notebook: Optional[ttk.Notebook] = None
self.templates_combobox: Optional[ttk.Combobox] = None
@ -91,25 +89,18 @@ class ConfigServiceConfigDialog(Dialog):
response = self.core.client.get_config_service_defaults(self.service_name)
self.original_service_files = response.templates
self.temp_service_files = dict(self.original_service_files)
self.modes = sorted(x.name for x in response.modes)
self.mode_configs = {x.name: x.config for x in response.modes}
service_config = self.canvas_node.config_service_configs.get(
self.service_name, {}
)
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:
for key, value in custom_config.items():
service_config = self.node.config_service_configs.get(self.service_name)
if service_config:
for key, value in service_config.config.items():
self.config[key].value = value
logging.info("default config: %s", self.default_config)
custom_templates = service_config.get("templates", {})
for file, data in custom_templates.items():
self.modified_files.add(file)
self.temp_service_files[file] = data
logging.info("default config: %s", self.default_config)
for file, data in service_config.templates.items():
self.modified_files.add(file)
self.temp_service_files[file] = data
except grpc.RpcError as e:
self.app.show_grpc_exception("Get Config Service Error", e)
self.has_error = True
@ -313,20 +304,18 @@ class ConfigServiceConfigDialog(Dialog):
def click_apply(self) -> None:
current_listbox = self.master.current.listbox
if not self.is_custom():
self.canvas_node.config_service_configs.pop(self.service_name, None)
self.node.config_service_configs.pop(self.service_name, None)
current_listbox.itemconfig(current_listbox.curselection()[0], bg="")
self.destroy()
return
service_config = self.canvas_node.config_service_configs.setdefault(
self.service_name, {}
)
service_config = self.node.config_service_configs.get(self.service_name)
if not service_config:
service_config = ConfigServiceData()
if self.config_frame:
self.config_frame.parse_config()
service_config["config"] = {x.name: x.value for x in self.config.values()}
templates_config = service_config.setdefault("templates", {})
service_config.config = {x.name: x.value for x in self.config.values()}
for file in self.modified_files:
templates_config[file] = self.temp_service_files[file]
service_config.templates[file] = self.temp_service_files[file]
all_current = current_listbox.get(0, tk.END)
current_listbox.itemconfig(all_current.index(self.service_name), bg="green")
self.destroy()
@ -360,9 +349,9 @@ class ConfigServiceConfigDialog(Dialog):
return has_custom_templates or has_custom_config
def click_defaults(self) -> None:
self.canvas_node.config_service_configs.pop(self.service_name, None)
self.node.config_service_configs.pop(self.service_name, None)
logging.info(
"cleared config service config: %s", self.canvas_node.config_service_configs
"cleared config service config: %s", self.node.config_service_configs
)
self.temp_service_files = dict(self.original_service_files)
filename = self.templates_combobox.get()

View file

@ -43,16 +43,15 @@ class CopyServiceConfigDialog(Dialog):
listbox_scroll = ListboxScroll(self.top)
listbox_scroll.grid(sticky="nsew", pady=PADY)
self.listbox = listbox_scroll.listbox
for canvas_node in self.app.canvas.nodes.values():
file_configs = canvas_node.service_file_configs.get(self.service)
for node in self.app.core.session.nodes.values():
file_configs = node.service_file_configs.get(self.service)
if not file_configs:
continue
data = file_configs.get(self.file_name)
if not data:
continue
name = canvas_node.core_node.name
self.nodes[name] = canvas_node.id
self.listbox.insert(tk.END, name)
self.nodes[node.name] = node.id
self.listbox.insert(tk.END, node.name)
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
@ -70,9 +69,9 @@ class CopyServiceConfigDialog(Dialog):
if not selection:
return
name = self.listbox.get(selection)
canvas_node_id = self.nodes[name]
canvas_node = self.app.canvas.nodes[canvas_node_id]
data = canvas_node.service_file_configs[self.service][self.file_name]
node_id = self.nodes[name]
node = self.app.core.session.nodes[node_id]
data = node.service_file_configs[self.service][self.file_name]
self.dialog.temp_service_files[self.file_name] = data
self.dialog.modified_files.add(self.file_name)
self.dialog.service_file_data.text.delete(1.0, tk.END)
@ -84,9 +83,9 @@ class CopyServiceConfigDialog(Dialog):
if not selection:
return
name = self.listbox.get(selection)
canvas_node_id = self.nodes[name]
canvas_node = self.app.canvas.nodes[canvas_node_id]
data = canvas_node.service_file_configs[self.service][self.file_name]
node_id = self.nodes[name]
node = self.app.core.session.nodes[node_id]
data = node.service_file_configs[self.service][self.file_name]
dialog = ViewConfigDialog(
self.app, self, name, self.service, self.file_name, data
)

View file

@ -16,7 +16,6 @@ from core.gui.wrappers import ConfigOption, Node
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
class GlobalEmaneDialog(Dialog):
@ -29,8 +28,9 @@ class GlobalEmaneDialog(Dialog):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
session = self.app.core.session
self.config_frame = ConfigFrame(
self.top, self.app, self.app.core.emane_config, self.enabled
self.top, self.app, session.emane_config, self.enabled
)
self.config_frame.draw_config()
self.config_frame.grid(sticky="nsew", pady=PADY)
@ -58,24 +58,19 @@ class EmaneModelDialog(Dialog):
self,
master: tk.BaseWidget,
app: "Application",
canvas_node: "CanvasNode",
node: Node,
model: str,
iface_id: int = None,
) -> None:
super().__init__(
app, f"{canvas_node.core_node.name} {model} Configuration", master=master
)
self.canvas_node: "CanvasNode" = canvas_node
self.node: Node = canvas_node.core_node
super().__init__(app, f"{node.name} {model} Configuration", master=master)
self.node: Node = node
self.model: str = f"emane_{model}"
self.iface_id: int = iface_id
self.config_frame: Optional[ConfigFrame] = None
self.enabled: bool = not self.app.core.is_runtime()
self.has_error: bool = False
try:
config = self.canvas_node.emane_model_configs.get(
(self.model, self.iface_id)
)
config = self.node.emane_model_configs.get((self.model, self.iface_id))
if not config:
config = self.app.core.get_emane_model_config(
self.node.id, self.model, self.iface_id
@ -110,19 +105,18 @@ class EmaneModelDialog(Dialog):
def click_apply(self) -> None:
self.config_frame.parse_config()
key = (self.model, self.iface_id)
self.canvas_node.emane_model_configs[key] = self.config
self.node.emane_model_configs[key] = self.config
self.destroy()
class EmaneConfigDialog(Dialog):
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
super().__init__(app, f"{canvas_node.core_node.name} EMANE Configuration")
self.canvas_node: "CanvasNode" = canvas_node
self.node: Node = canvas_node.core_node
def __init__(self, app: "Application", node: Node) -> None:
super().__init__(app, f"{node.name} EMANE Configuration")
self.node: Node = node
self.radiovar: tk.IntVar = tk.IntVar()
self.radiovar.set(1)
self.emane_models: List[str] = [
x.split("_")[1] for x in self.app.core.emane_models
x.split("_")[1] for x in self.app.core.session.emane_models
]
model = self.node.emane.split("_")[1]
self.emane_model: tk.StringVar = tk.StringVar(value=model)
@ -231,7 +225,7 @@ class EmaneConfigDialog(Dialog):
draw emane model configuration
"""
model_name = self.emane_model.get()
dialog = EmaneModelDialog(self, self.app, self.canvas_node, model_name)
dialog = EmaneModelDialog(self, self.app, self.node, model_name)
if not dialog.has_error:
dialog.show()

View file

@ -113,8 +113,9 @@ class HooksDialog(Dialog):
listbox_scroll.grid(sticky="nsew", pady=PADY)
self.listbox = listbox_scroll.listbox
self.listbox.bind("<<ListboxSelect>>", self.select)
for hook_file in self.app.core.hooks:
self.listbox.insert(tk.END, hook_file)
session = self.app.core.session
for file in session.hooks:
self.listbox.insert(tk.END, file)
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
@ -138,20 +139,22 @@ class HooksDialog(Dialog):
dialog.show()
hook = dialog.hook
if hook:
self.app.core.hooks[hook.file] = hook
self.app.core.session.hooks[hook.file] = hook
self.listbox.insert(tk.END, hook.file)
def click_edit(self) -> None:
hook = self.app.core.hooks.pop(self.selected)
session = self.app.core.session
hook = session.hooks.pop(self.selected)
dialog = HookDialog(self, self.app)
dialog.set(hook)
dialog.show()
self.app.core.hooks[hook.file] = hook
session.hooks[hook.file] = hook
self.listbox.delete(self.selected_index)
self.listbox.insert(self.selected_index, hook.file)
def click_delete(self) -> None:
del self.app.core.hooks[self.selected]
session = self.app.core.session
del session.hooks[self.selected]
self.listbox.delete(tk.ANCHOR)
self.edit_button.config(state=tk.DISABLED)
self.delete_button.config(state=tk.DISABLED)

View file

@ -269,7 +269,7 @@ class LinkConfigurationDialog(Dialog):
self.edge.asymmetric_link = None
if self.app.core.is_runtime() and link.options:
session_id = self.app.core.session_id
session_id = self.app.core.session.id
self.app.core.client.edit_link(
session_id,
link.node1_id,

View file

@ -13,18 +13,16 @@ from core.gui.wrappers import ConfigOption, Node
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
class MobilityConfigDialog(Dialog):
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
super().__init__(app, f"{canvas_node.core_node.name} Mobility Configuration")
self.canvas_node: "CanvasNode" = canvas_node
self.node: Node = canvas_node.core_node
def __init__(self, app: "Application", node: Node) -> None:
super().__init__(app, f"{node.name} Mobility Configuration")
self.node: Node = node
self.config_frame: Optional[ConfigFrame] = None
self.has_error: bool = False
try:
config = self.canvas_node.mobility_config
config = self.node.mobility_config
if not config:
config = self.app.core.get_mobility_config(self.node.id)
self.config: Dict[str, ConfigOption] = config
@ -56,5 +54,5 @@ class MobilityConfigDialog(Dialog):
def click_apply(self) -> None:
self.config_frame.parse_config()
self.canvas_node.mobility_config = self.config
self.node.mobility_config = self.config
self.destroy()

View file

@ -1,38 +1,31 @@
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING, Dict, Optional
from typing import TYPE_CHECKING, Optional
import grpc
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, MobilityAction, Node
from core.gui.wrappers import MobilityAction, Node
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
ICON_SIZE: int = 16
class MobilityPlayer:
def __init__(
self,
app: "Application",
canvas_node: "CanvasNode",
config: Dict[str, ConfigOption],
) -> None:
def __init__(self, app: "Application", node: Node) -> None:
self.app: "Application" = app
self.canvas_node: "CanvasNode" = canvas_node
self.config: Dict[str, ConfigOption] = config
self.node: Node = node
self.dialog: Optional[MobilityPlayerDialog] = None
self.state: Optional[MobilityAction] = None
def show(self) -> None:
if self.dialog:
self.dialog.destroy()
self.dialog = MobilityPlayerDialog(self.app, self.canvas_node, self.config)
self.dialog = MobilityPlayerDialog(self.app, self.node)
self.dialog.protocol("WM_DELETE_WINDOW", self.close)
if self.state == MobilityAction.START:
self.set_play()
@ -64,20 +57,11 @@ class MobilityPlayer:
class MobilityPlayerDialog(Dialog):
def __init__(
self,
app: "Application",
canvas_node: "CanvasNode",
config: Dict[str, ConfigOption],
) -> None:
super().__init__(
app, f"{canvas_node.core_node.name} Mobility Player", modal=False
)
def __init__(self, app: "Application", node: Node) -> None:
super().__init__(app, f"{node.name} Mobility Player", modal=False)
self.resizable(False, False)
self.geometry("")
self.canvas_node: "CanvasNode" = canvas_node
self.node: Node = canvas_node.core_node
self.config: Dict[str, ConfigOption] = config
self.node: Node = node
self.play_button: Optional[ttk.Button] = None
self.pause_button: Optional[ttk.Button] = None
self.stop_button: Optional[ttk.Button] = None
@ -85,9 +69,10 @@ class MobilityPlayerDialog(Dialog):
self.draw()
def draw(self) -> None:
config = self.node.mobility_config
self.top.columnconfigure(0, weight=1)
file_name = self.config["file"].value
file_name = config["file"].value
label = ttk.Label(self.top, text=file_name)
label.grid(sticky="ew", pady=PADY)
@ -114,13 +99,13 @@ class MobilityPlayerDialog(Dialog):
self.stop_button.image = image
self.stop_button.grid(row=0, column=2, sticky="ew", padx=PADX)
loop = tk.IntVar(value=int(self.config["loop"].value == "1"))
loop = tk.IntVar(value=int(config["loop"].value == "1"))
checkbutton = ttk.Checkbutton(
frame, text="Loop?", variable=loop, state=tk.DISABLED
)
checkbutton.grid(row=0, column=3, padx=PADX)
rate = self.config["refresh_ms"].value
rate = config["refresh_ms"].value
label = ttk.Label(frame, text=f"rate {rate} ms")
label.grid(row=0, column=4)
@ -146,7 +131,7 @@ class MobilityPlayerDialog(Dialog):
def click_play(self) -> None:
self.set_play()
session_id = self.app.core.session_id
session_id = self.app.core.session.id
try:
self.app.core.client.mobility_action(
session_id, self.node.id, MobilityAction.START.value
@ -156,7 +141,7 @@ class MobilityPlayerDialog(Dialog):
def click_pause(self) -> None:
self.set_pause()
session_id = self.app.core.session_id
session_id = self.app.core.session.id
try:
self.app.core.client.mobility_action(
session_id, self.node.id, MobilityAction.PAUSE.value
@ -166,7 +151,7 @@ class MobilityPlayerDialog(Dialog):
def click_stop(self) -> None:
self.set_stop()
session_id = self.app.core.session_id
session_id = self.app.core.session.id
try:
self.app.core.client.mobility_action(
session_id, self.node.id, MobilityAction.STOP.value

View file

@ -10,25 +10,24 @@ from core.gui.dialogs.configserviceconfig import ConfigServiceConfigDialog
from core.gui.dialogs.dialog import Dialog
from core.gui.themes import FRAME_PAD, PADX, PADY
from core.gui.widgets import CheckboxList, ListboxScroll
from core.gui.wrappers import Node
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
class NodeConfigServiceDialog(Dialog):
def __init__(
self, app: "Application", canvas_node: "CanvasNode", services: Set[str] = None
self, app: "Application", node: Node, services: Set[str] = None
) -> None:
title = f"{canvas_node.core_node.name} Config Services"
title = f"{node.name} Config Services"
super().__init__(app, title)
self.canvas_node: "CanvasNode" = canvas_node
self.node_id: int = canvas_node.core_node.id
self.node: Node = node
self.groups: Optional[ListboxScroll] = None
self.services: Optional[CheckboxList] = None
self.current: Optional[ListboxScroll] = None
if services is None:
services = set(canvas_node.core_node.config_services)
services = set(node.config_services)
self.current_services: Set[str] = services
self.draw()
@ -102,7 +101,7 @@ class NodeConfigServiceDialog(Dialog):
elif not var.get() and name in self.current_services:
self.current_services.remove(name)
self.draw_current_services()
self.canvas_node.core_node.config_services[:] = self.current_services
self.node.config_services[:] = self.current_services
def click_configure(self) -> None:
current_selection = self.current.listbox.curselection()
@ -111,8 +110,7 @@ class NodeConfigServiceDialog(Dialog):
self,
self.app,
self.current.listbox.get(current_selection[0]),
self.canvas_node,
self.node_id,
self.node,
)
if not dialog.has_error:
dialog.show()
@ -132,10 +130,8 @@ class NodeConfigServiceDialog(Dialog):
self.current.listbox.itemconfig(tk.END, bg="green")
def click_save(self) -> None:
self.canvas_node.core_node.config_services[:] = self.current_services
logging.info(
"saved node config services: %s", self.canvas_node.core_node.config_services
)
self.node.config_services[:] = self.current_services
logging.info("saved node config services: %s", self.node.config_services)
self.destroy()
def click_cancel(self) -> None:
@ -154,4 +150,4 @@ class NodeConfigServiceDialog(Dialog):
return
def is_custom_service(self, service: str) -> bool:
return service in self.canvas_node.config_service_configs
return service in self.node.config_service_configs

View file

@ -9,22 +9,21 @@ from core.gui.dialogs.dialog import Dialog
from core.gui.dialogs.serviceconfig import ServiceConfigDialog
from core.gui.themes import FRAME_PAD, PADX, PADY
from core.gui.widgets import CheckboxList, ListboxScroll
from core.gui.wrappers import Node
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
class NodeServiceDialog(Dialog):
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
title = f"{canvas_node.core_node.name} Services"
def __init__(self, app: "Application", node: Node) -> None:
title = f"{node.name} Services"
super().__init__(app, title)
self.canvas_node: "CanvasNode" = canvas_node
self.node_id: int = canvas_node.core_node.id
self.node: Node = node
self.groups: Optional[ListboxScroll] = None
self.services: Optional[CheckboxList] = None
self.current: Optional[ListboxScroll] = None
services = set(canvas_node.core_node.services)
services = set(node.services)
self.current_services: Set[str] = services
self.draw()
@ -104,7 +103,7 @@ class NodeServiceDialog(Dialog):
self.current.listbox.insert(tk.END, name)
if self.is_custom_service(name):
self.current.listbox.itemconfig(tk.END, bg="green")
self.canvas_node.core_node.services[:] = self.current_services
self.node.services = self.current_services.copy()
def click_configure(self) -> None:
current_selection = self.current.listbox.curselection()
@ -113,8 +112,7 @@ class NodeServiceDialog(Dialog):
self,
self.app,
self.current.listbox.get(current_selection[0]),
self.canvas_node,
self.node_id,
self.node,
)
# if error occurred when creating ServiceConfigDialog, don't show the dialog
@ -128,8 +126,7 @@ class NodeServiceDialog(Dialog):
)
def click_save(self) -> None:
core_node = self.canvas_node.core_node
core_node.services[:] = self.current_services
self.node.services[:] = self.current_services
self.destroy()
def click_remove(self) -> None:
@ -144,6 +141,6 @@ class NodeServiceDialog(Dialog):
return
def is_custom_service(self, service: str) -> bool:
has_service_config = service in self.canvas_node.service_configs
has_file_config = service in self.canvas_node.service_file_configs
has_service_config = service in self.node.service_configs
has_file_config = service in self.node.service_file_configs
return has_service_config or has_file_config

View file

@ -107,7 +107,7 @@ class RunToolDialog(Dialog):
node_name = self.node_list.listbox.get(selection)
node_id = self.executable_nodes[node_name]
response = self.app.core.client.node_command(
self.app.core.session_id, node_id, command
self.app.core.session.id, node_id, command
)
self.result.text.insert(
tk.END, f"> {node_name} > {command}:\n{response.output}\n"

View file

@ -12,11 +12,10 @@ 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
from core.gui.wrappers import Node, NodeServiceData, ServiceValidationMode
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
from core.gui.coreclient import CoreClient
ICON_SIZE: int = 16
@ -24,18 +23,12 @@ ICON_SIZE: int = 16
class ServiceConfigDialog(Dialog):
def __init__(
self,
master: tk.BaseWidget,
app: "Application",
service_name: str,
canvas_node: "CanvasNode",
node_id: int,
self, master: tk.BaseWidget, app: "Application", service_name: str, node: Node
) -> None:
title = f"{service_name} Service"
super().__init__(app, title, master=master)
self.core: "CoreClient" = app.core
self.canvas_node: "CanvasNode" = canvas_node
self.node_id: int = node_id
self.node: Node = node
self.service_name: str = service_name
self.radiovar: tk.IntVar = tk.IntVar(value=2)
self.metadata: str = ""
@ -84,15 +77,13 @@ class ServiceConfigDialog(Dialog):
try:
self.app.core.create_nodes_and_links()
default_config = self.app.core.get_node_service(
self.node_id, self.service_name
self.node.id, self.service_name
)
self.default_startup = default_config.startup[:]
self.default_validate = default_config.validate[:]
self.default_shutdown = default_config.shutdown[:]
self.default_directories = default_config.dirs[:]
custom_service_config = self.canvas_node.service_configs.get(
self.service_name
)
custom_service_config = self.node.service_configs.get(self.service_name)
self.default_config = default_config
service_config = (
custom_service_config if custom_service_config else default_config
@ -109,15 +100,13 @@ class ServiceConfigDialog(Dialog):
self.temp_directories = service_config.dirs[:]
self.original_service_files = {
x: self.app.core.get_node_service_file(
self.node_id, self.service_name, x
self.node.id, self.service_name, x
)
for x in default_config.configs
}
self.temp_service_files = dict(self.original_service_files)
file_configs = self.canvas_node.service_file_configs.get(
self.service_name, {}
)
file_configs = self.node.service_file_configs.get(self.service_name, {})
for file, data in file_configs.items():
self.temp_service_files[file] = data
except grpc.RpcError as e:
@ -453,7 +442,7 @@ class ServiceConfigDialog(Dialog):
and not self.has_new_files()
and not self.is_custom_directory()
):
self.canvas_node.service_configs.pop(self.service_name, None)
self.node.service_configs.pop(self.service_name, None)
self.current_service_color("")
self.destroy()
return
@ -466,7 +455,7 @@ class ServiceConfigDialog(Dialog):
):
startup, validate, shutdown = self.get_commands()
config = self.core.set_node_service(
self.node_id,
self.node.id,
self.service_name,
dirs=self.temp_directories,
files=list(self.filename_combobox["values"]),
@ -474,15 +463,15 @@ class ServiceConfigDialog(Dialog):
validations=validate,
shutdowns=shutdown,
)
self.canvas_node.service_configs[self.service_name] = config
self.node.service_configs[self.service_name] = config
for file in self.modified_files:
file_configs = self.canvas_node.service_file_configs.setdefault(
file_configs = self.node.service_file_configs.setdefault(
self.service_name, {}
)
file_configs[file] = self.temp_service_files[file]
# TODO: check if this is really needed
self.app.core.set_node_service_file(
self.node_id, self.service_name, file, self.temp_service_files[file]
self.node.id, self.service_name, file, self.temp_service_files[file]
)
self.current_service_color("green")
except grpc.RpcError as e:
@ -526,8 +515,8 @@ class ServiceConfigDialog(Dialog):
clears out any custom configuration permanently
"""
# clear coreclient data
self.canvas_node.service_configs.pop(self.service_name, None)
file_configs = self.canvas_node.service_file_configs.pop(self.service_name, {})
self.node.service_configs.pop(self.service_name, None)
file_configs = self.node.service_file_configs.pop(self.service_name, {})
file_configs.pop(self.service_name, None)
self.temp_service_files = dict(self.original_service_files)
self.modified_files.clear()
@ -564,9 +553,8 @@ class ServiceConfigDialog(Dialog):
def click_copy(self) -> None:
file_name = self.filename_combobox.get()
name = self.canvas_node.core_node.name
dialog = CopyServiceConfigDialog(
self.app, self, name, self.service_name, file_name
self.app, self, self.node.name, self.service_name, file_name
)
dialog.show()

View file

@ -26,7 +26,7 @@ class SessionOptionsDialog(Dialog):
def get_config(self) -> Dict[str, ConfigOption]:
try:
session_id = self.app.core.session_id
session_id = self.app.core.session.id
response = self.app.core.client.get_session_options(session_id)
return ConfigOption.from_dict(response.config)
except grpc.RpcError as e:
@ -54,7 +54,7 @@ class SessionOptionsDialog(Dialog):
def save(self) -> None:
config = self.config_frame.parse_config()
try:
session_id = self.app.core.session_id
session_id = self.app.core.session.id
response = self.app.core.client.set_session_options(session_id, config)
logging.info("saved session config: %s", response)
except grpc.RpcError as e:

View file

@ -201,7 +201,7 @@ class SessionsDialog(Dialog):
logging.debug("delete session: %s", self.selected_session)
self.tree.delete(self.selected_id)
self.app.core.delete_session(self.selected_session)
if self.selected_session == self.app.core.session_id:
if self.selected_session == self.app.core.session.id:
self.click_new()
self.destroy()
self.click_select()

View file

@ -29,7 +29,7 @@ class WlanConfigDialog(Dialog):
self.ranges: Dict[int, int] = {}
self.positive_int: int = self.app.master.register(self.validate_and_update)
try:
config = self.canvas_node.wlan_config
config = self.node.wlan_config
if not config:
config = self.app.core.get_wlan_config(self.node.id)
self.config: Dict[str, ConfigOption] = config
@ -83,9 +83,9 @@ class WlanConfigDialog(Dialog):
retrieve user's wlan configuration and store the new configuration values
"""
config = self.config_frame.parse_config()
self.canvas_node.wlan_config = self.config
self.node.wlan_config = self.config
if self.app.core.is_runtime():
session_id = self.app.core.session_id
session_id = self.app.core.session.id
self.app.core.client.set_wlan_config(session_id, self.node.id, config)
self.remove_ranges()
self.destroy()

View file

@ -940,16 +940,19 @@ class CanvasGraph(tk.Canvas):
if not copy:
continue
node = CanvasNode(self.app, scaled_x, scaled_y, copy, canvas_node.image)
# copy configurations and services
node.core_node.services[:] = canvas_node.core_node.services
node.core_node.config_services[:] = canvas_node.core_node.config_services
node.emane_model_configs = deepcopy(canvas_node.emane_model_configs)
node.wlan_config = deepcopy(canvas_node.wlan_config)
node.mobility_config = deepcopy(canvas_node.mobility_config)
node.service_configs = deepcopy(canvas_node.service_configs)
node.service_file_configs = deepcopy(canvas_node.service_file_configs)
node.config_service_configs = deepcopy(canvas_node.config_service_configs)
node.core_node.services = core_node.services.copy()
node.core_node.config_services = core_node.config_services.copy()
node.core_node.emane_model_configs = deepcopy(core_node.emane_model_configs)
node.core_node.wlan_config = deepcopy(core_node.wlan_config)
node.core_node.mobility_config = deepcopy(core_node.mobility_config)
node.core_node.service_configs = deepcopy(core_node.service_configs)
node.core_node.service_file_configs = deepcopy(
core_node.service_file_configs
)
node.core_node.config_service_configs = deepcopy(
core_node.config_service_configs
)
copy_map[canvas_node.id] = node.id
self.core.canvas_nodes[copy.id] = node

View file

@ -1,7 +1,7 @@
import functools
import logging
import tkinter as tk
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
from typing import TYPE_CHECKING, Dict, List, Set
import grpc
from PIL.ImageTk import PhotoImage
@ -19,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, NodeServiceData, NodeType
from core.gui.wrappers import Interface, Node, NodeType
if TYPE_CHECKING:
from core.gui.app import Application
@ -56,15 +56,6 @@ class CanvasNode:
self.wireless_edges: Set[CanvasWirelessEdge] = set()
self.antennas: List[int] = []
self.antenna_images: Dict[int, PhotoImage] = {}
# possible configurations
self.emane_model_configs: Dict[
Tuple[str, Optional[int]], Dict[str, ConfigOption]
] = {}
self.wlan_config: Dict[str, ConfigOption] = {}
self.mobility_config: Dict[str, ConfigOption] = {}
self.service_configs: Dict[str, NodeServiceData] = {}
self.service_file_configs: Dict[str, Dict[str, str]] = {}
self.config_service_configs: Dict[str, Any] = {}
self.setup_bindings()
self.context: tk.Menu = tk.Menu(self.canvas)
themes.style_menu(self.context)
@ -299,7 +290,7 @@ class CanvasNode:
dialog.show()
def show_mobility_config(self) -> None:
dialog = MobilityConfigDialog(self.app, self)
dialog = MobilityConfigDialog(self.app, self.core_node)
if not dialog.has_error:
dialog.show()
@ -308,15 +299,15 @@ class CanvasNode:
mobility_player.show()
def show_emane_config(self) -> None:
dialog = EmaneConfigDialog(self.app, self)
dialog = EmaneConfigDialog(self.app, self.core_node)
dialog.show()
def show_services(self) -> None:
dialog = NodeServiceDialog(self.app, self)
dialog = NodeServiceDialog(self.app, self.core_node)
dialog.show()
def show_config_services(self) -> None:
dialog = NodeConfigServiceDialog(self.app, self)
dialog = NodeConfigServiceDialog(self.app, self.core_node)
dialog.show()
def has_emane_link(self, iface_id: int) -> Node:

View file

@ -33,7 +33,6 @@ class ProgressTask:
thread.start()
def run(self) -> None:
logging.info("running task")
try:
values = self.task(*self.args)
if values is None:
@ -41,7 +40,6 @@ class ProgressTask:
elif values and not isinstance(values, tuple):
values = (values,)
if self.callback:
logging.info("calling callback")
self.app.after(0, self.callback, *values)
except Exception as e:
logging.exception("progress task exception")

View file

@ -1,6 +1,6 @@
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Optional, Tuple
from typing import Dict, List, Optional, Set, Tuple
from core.api.grpc import common_pb2, configservices_pb2, core_pb2, services_pb2
@ -121,8 +121,8 @@ class ConfigService:
@dataclass
class ConfigServiceData:
templates: Dict[str, str]
config: Dict[str, str]
templates: Dict[str, str] = field(default_factory=dict)
config: Dict[str, str] = field(default_factory=dict)
@dataclass
@ -504,8 +504,8 @@ class Node:
type: NodeType
model: str = None
position: Position = None
services: List[str] = field(default_factory=list)
config_services: List[str] = field(default_factory=list)
services: Set[str] = field(default_factory=set)
config_services: Set[str] = field(default_factory=set)
emane: str = None
icon: str = None
image: str = None
@ -538,8 +538,8 @@ class Node:
type=NodeType(proto.type),
model=proto.model,
position=Position.from_proto(proto.position),
services=list(proto.services),
config_services=list(proto.config_services),
services=set(proto.services),
config_services=set(proto.config_services),
emane=proto.emane,
icon=proto.icon,
image=proto.image,
@ -575,9 +575,9 @@ class Session:
links: List[Link]
dir: str
user: str
default_services: Dict[str, List[str]]
default_services: Dict[str, Set[str]]
location: SessionLocation
hooks: List[Hook]
hooks: Dict[str, Hook]
emane_models: List[str]
emane_config: Dict[str, ConfigOption]
metadata: Dict[str, str]
@ -586,8 +586,10 @@ class Session:
def from_proto(cls, proto: core_pb2.Session) -> "Session":
nodes: Dict[int, Node] = {x.id: Node.from_proto(x) for x in proto.nodes}
links = [Link.from_proto(x) for x in proto.links]
default_services = {x.node_type: x.services for x in proto.default_services}
hooks = [Hook.from_proto(x) for x in proto.hooks]
default_services = {
x.node_type: set(x.services) for x in proto.default_services
}
hooks = {x.file: Hook.from_proto(x) for x in proto.hooks}
# update nodes with their current configurations
for model in proto.emane_model_configs:
iface_id = None