From b04da98f4480aefc5f8fb9880a8c1b38cab88b32 Mon Sep 17 00:00:00 2001
From: Blake Harnden <32446120+bharnden@users.noreply.github.com>
Date: Tue, 21 Apr 2020 11:13:41 -0700
Subject: [PATCH] pygui updated config services to be associated with nodes
 directly and copyable

---
 daemon/core/gui/coreclient.py                 | 16 +++++----
 .../core/gui/dialogs/configserviceconfig.py   | 32 ++++++++++-------
 daemon/core/gui/dialogs/nodeconfigservice.py  | 36 +++++++++----------
 daemon/core/gui/graph/graph.py                |  2 ++
 daemon/core/gui/graph/node.py                 |  1 +
 5 files changed, 49 insertions(+), 38 deletions(-)

diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py
index e026b44b..7c69f954 100644
--- a/daemon/core/gui/coreclient.py
+++ b/daemon/core/gui/coreclient.py
@@ -91,7 +91,6 @@ class CoreClient:
         self.links = {}
         self.hooks = {}
         self.emane_config = None
-        self.config_service_configs = {}
         self.mobility_players = {}
         self.handling_throughputs = None
         self.handling_events = None
@@ -341,10 +340,10 @@ class CoreClient:
             # get config service configurations
             response = self.client.get_node_config_service_configs(self.session_id)
             for config in response.configs:
-                node_configs = self.config_service_configs.setdefault(
-                    config.node_id, {}
+                canvas_node = self.canvas_nodes[config.node_id]
+                service_config = canvas_node.config_service_configs.setdefault(
+                    config.name, {}
                 )
-                service_config = node_configs.setdefault(config.name, {})
                 if config.templates:
                     service_config["templates"] = config.templates
                 if config.config:
@@ -989,8 +988,13 @@ class CoreClient:
         self
     ) -> List[configservices_pb2.ConfigServiceConfig]:
         config_service_protos = []
-        for node_id, node_config in self.config_service_configs.items():
-            for name, service_config in node_config.items():
+        for canvas_node in self.canvas_nodes.values():
+            if not NodeUtils.is_container_node(canvas_node.core_node.type):
+                continue
+            if not canvas_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", {})
                 config_proto = configservices_pb2.ConfigServiceConfig(
                     node_id=node_id,
diff --git a/daemon/core/gui/dialogs/configserviceconfig.py b/daemon/core/gui/dialogs/configserviceconfig.py
index 36c4ccfe..2239034e 100644
--- a/daemon/core/gui/dialogs/configserviceconfig.py
+++ b/daemon/core/gui/dialogs/configserviceconfig.py
@@ -16,21 +16,26 @@ from core.gui.widgets import CodeText, ConfigFrame, ListboxScroll
 
 if TYPE_CHECKING:
     from core.gui.app import Application
+    from core.gui.graph.node import CanvasNode
 
 
 class ConfigServiceConfigDialog(Dialog):
     def __init__(
-        self, master: Any, app: "Application", service_name: str, node_id: int
+        self,
+        master: Any,
+        app: "Application",
+        service_name: str,
+        canvas_node: "CanvasNode",
+        node_id: int,
     ):
         title = f"{service_name} Config Service"
         super().__init__(master, app, title, modal=True)
         self.master = master
         self.app = app
         self.core = app.core
+        self.canvas_node = canvas_node
         self.node_id = node_id
         self.service_name = service_name
-        self.service_configs = app.core.config_service_configs
-
         self.radiovar = tk.IntVar()
         self.radiovar.set(2)
         self.directories = []
@@ -95,9 +100,9 @@ class ConfigServiceConfigDialog(Dialog):
             self.modes = sorted(x.name for x in response.modes)
             self.mode_configs = {x.name: x.config for x in response.modes}
 
-            node_configs = self.service_configs.get(self.node_id, {})
-            service_config = node_configs.get(self.service_name, {})
-
+            service_config = self.canvas_node.config_service_configs.get(
+                self.service_name, {}
+            )
             self.config = response.config
             self.default_config = {x.name: x.value for x in self.config.values()}
             custom_config = service_config.get("config")
@@ -313,15 +318,15 @@ class ConfigServiceConfigDialog(Dialog):
     def click_apply(self):
         current_listbox = self.master.current.listbox
         if not self.is_custom():
-            if self.node_id in self.service_configs:
-                self.service_configs[self.node_id].pop(self.service_name, None)
+            self.canvas_node.config_service_configs.pop(self.service_name, None)
             current_listbox.itemconfig(current_listbox.curselection()[0], bg="")
             self.destroy()
             return
 
         try:
-            node_config = self.service_configs.setdefault(self.node_id, {})
-            service_config = node_config.setdefault(self.service_name, {})
+            service_config = self.canvas_node.config_service_configs.setdefault(
+                self.service_name, {}
+            )
             if self.config_frame:
                 self.config_frame.parse_config()
                 service_config["config"] = {
@@ -365,9 +370,10 @@ class ConfigServiceConfigDialog(Dialog):
         return has_custom_templates or has_custom_config
 
     def click_defaults(self):
-        if self.node_id in self.service_configs:
-            node_config = self.service_configs.get(self.node_id, {})
-            node_config.pop(self.service_name, None)
+        self.canvas_node.config_service_configs.pop(self.service_name, None)
+        logging.info(
+            "cleared config service config: %s", self.canvas_node.config_service_configs
+        )
         self.temp_service_files = dict(self.original_service_files)
         filename = self.templates_combobox.get()
         self.template_text.text.delete(1.0, "end")
diff --git a/daemon/core/gui/dialogs/nodeconfigservice.py b/daemon/core/gui/dialogs/nodeconfigservice.py
index 8bdbc539..c86d8887 100644
--- a/daemon/core/gui/dialogs/nodeconfigservice.py
+++ b/daemon/core/gui/dialogs/nodeconfigservice.py
@@ -70,12 +70,10 @@ class NodeConfigServiceDialog(Dialog):
         label_frame.grid(row=0, column=2, sticky="nsew")
         label_frame.rowconfigure(0, weight=1)
         label_frame.columnconfigure(0, weight=1)
+
         self.current = ListboxScroll(label_frame)
         self.current.grid(sticky="nsew")
-        for service in sorted(self.current_services):
-            self.current.listbox.insert(tk.END, service)
-            if self.is_custom_service(service):
-                self.current.listbox.itemconfig(tk.END, bg="green")
+        self.draw_current_services()
 
         frame = ttk.Frame(self.top)
         frame.grid(stick="ew")
@@ -108,24 +106,22 @@ class NodeConfigServiceDialog(Dialog):
             self.current_services.add(name)
         elif not var.get() and name in self.current_services:
             self.current_services.remove(name)
-        self.current.listbox.delete(0, tk.END)
-        for name in sorted(self.current_services):
-            self.current.listbox.insert(tk.END, name)
-            if self.is_custom_service(name):
-                self.current.listbox.itemconfig(tk.END, bg="green")
+        self.draw_current_services()
         self.canvas_node.core_node.config_services[:] = self.current_services
 
     def click_configure(self):
         current_selection = self.current.listbox.curselection()
         if len(current_selection):
             dialog = ConfigServiceConfigDialog(
-                master=self,
-                app=self.app,
-                service_name=self.current.listbox.get(current_selection[0]),
-                node_id=self.node_id,
+                self,
+                self.app,
+                self.current.listbox.get(current_selection[0]),
+                self.canvas_node,
+                self.node_id,
             )
             if not dialog.has_error:
                 dialog.show()
+                self.draw_current_services()
         else:
             messagebox.showinfo(
                 "Config Service Configuration",
@@ -133,6 +129,13 @@ class NodeConfigServiceDialog(Dialog):
                 parent=self,
             )
 
+    def draw_current_services(self):
+        self.current.listbox.delete(0, tk.END)
+        for name in sorted(self.current_services):
+            self.current.listbox.insert(tk.END, name)
+            if self.is_custom_service(name):
+                self.current.listbox.itemconfig(tk.END, bg="green")
+
     def click_save(self):
         self.canvas_node.core_node.config_services[:] = self.current_services
         logging.info(
@@ -156,9 +159,4 @@ class NodeConfigServiceDialog(Dialog):
                     return
 
     def is_custom_service(self, service: str) -> bool:
-        node_configs = self.app.core.config_service_configs.get(self.node_id, {})
-        service_config = node_configs.get(service)
-        if node_configs and service_config:
-            return True
-        else:
-            return False
+        return service in self.canvas_node.config_service_configs
diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py
index 5d66667e..e4963f45 100644
--- a/daemon/core/gui/graph/graph.py
+++ b/daemon/core/gui/graph/graph.py
@@ -925,11 +925,13 @@ class CanvasGraph(tk.Canvas):
 
             # 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)
 
             # add new node to modified_service_nodes set if that set contains the
             # to_copy node
diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py
index e788b584..90896284 100644
--- a/daemon/core/gui/graph/node.py
+++ b/daemon/core/gui/graph/node.py
@@ -64,6 +64,7 @@ class CanvasNode:
         self.mobility_config = {}
         self.service_configs = {}
         self.service_file_configs = {}
+        self.config_service_configs = {}
         self.setup_bindings()
 
     def setup_bindings(self):