From 13a7aa73a1197ff18927b8d427683cd2d64436fb Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Mon, 11 Nov 2019 10:57:26 -0800 Subject: [PATCH] draw node service configurations --- coretk/coretk/coreclient.py | 15 ++ coretk/coretk/dialogs/nodeconfig.py | 4 +- coretk/coretk/dialogs/nodeservice.py | 202 +++++++----------- coretk/coretk/dialogs/serviceconfiguration.py | 185 ++++++++++++++++ coretk/coretk/servicenodeconfig.py | 37 ++++ 5 files changed, 312 insertions(+), 131 deletions(-) create mode 100644 coretk/coretk/dialogs/serviceconfiguration.py create mode 100644 coretk/coretk/servicenodeconfig.py diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index f018a348..05a2b1e9 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -11,6 +11,7 @@ from coretk.emaneodelnodeconfig import EmaneModelNodeConfig from coretk.images import Images from coretk.interface import Interface, InterfaceManager from coretk.mobilitynodeconfig import MobilityNodeConfig +from coretk.servicenodeconfig import ServiceNodeConfig from coretk.wlannodeconfig import WlanNodeConfig link_layer_nodes = ["switch", "hub", "wlan", "rj45", "tunnel", "emane"] @@ -104,6 +105,7 @@ class CoreClient: self.mobilityconfig_management = MobilityNodeConfig() self.emaneconfig_management = EmaneModelNodeConfig(app) self.emane_config = None + self.serviceconfig_manager = ServiceNodeConfig(app) def read_config(self): # read distributed server @@ -327,6 +329,13 @@ class CoreClient: ) logging.debug("Start session %s, result: %s", self.session_id, response.result) + response = self.client.get_service_defaults(self.session_id) + for default in response.defaults: + print(default.node_type) + print(default.services) + response = self.client.get_node_service(self.session_id, 5, "FTP") + print(response) + def stop_session(self): response = self.client.stop_session(session_id=self.session_id) logging.debug("coregrpc.py Stop session, result: %s", response.result) @@ -453,6 +462,12 @@ class CoreClient: if node_type == core_pb2.NodeType.EMANE: self.emaneconfig_management.set_default_config(nid) + # set default service configurations + if node_type == core_pb2.NodeType.DEFAULT: + self.serviceconfig_manager.node_default_services_configuration( + node_id=nid, node_model=node_model + ) + self.nodes[canvas_id] = create_node self.core_mapping.map_core_id_to_canvas_id(nid, canvas_id) logging.debug( diff --git a/coretk/coretk/dialogs/nodeconfig.py b/coretk/coretk/dialogs/nodeconfig.py index 3f13488a..89dd7d11 100644 --- a/coretk/coretk/dialogs/nodeconfig.py +++ b/coretk/coretk/dialogs/nodeconfig.py @@ -3,7 +3,7 @@ from tkinter import ttk from coretk.dialogs.dialog import Dialog from coretk.dialogs.icondialog import IconDialog -from coretk.dialogs.nodeservice import NodeServicesDialog +from coretk.dialogs.nodeservice import NodeService NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"] DEFAULTNODES = ["router", "host", "PC"] @@ -87,7 +87,7 @@ class NodeConfigDialog(Dialog): button.grid(row=0, column=1, sticky="ew") def click_services(self): - dialog = NodeServicesDialog(self, self.app, self.canvas_node) + dialog = NodeService(self, self.app, self.canvas_node) dialog.show() def click_icon(self): diff --git a/coretk/coretk/dialogs/nodeservice.py b/coretk/coretk/dialogs/nodeservice.py index 6d92b98e..52421ca0 100644 --- a/coretk/coretk/dialogs/nodeservice.py +++ b/coretk/coretk/dialogs/nodeservice.py @@ -5,154 +5,98 @@ import tkinter as tk from tkinter import messagebox from coretk.dialogs.dialog import Dialog +from coretk.dialogs.serviceconfiguration import ServiceConfiguration +from coretk.widgets import CheckboxList, ListboxScroll -class NodeServicesDialog(Dialog): - def __init__(self, master, app, canvas_node): +class NodeService(Dialog): + def __init__(self, master, app, canvas_node, current_services=set()): super().__init__(master, app, "Node Services", modal=True) self.canvas_node = canvas_node - self.core_groups = [] - self.service_to_config = None - self.config_frame = None - self.services_list = None + self.groups = None + self.services = None + self.current = None + self.current_services = current_services self.draw() def draw(self): self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) - self.config_frame = tk.Frame(self) - self.config_frame.columnconfigure(0, weight=1) - self.config_frame.columnconfigure(1, weight=1) - self.config_frame.columnconfigure(2, weight=1) - self.config_frame.rowconfigure(0, weight=1) - self.config_frame.grid(row=0, column=0, sticky="nsew") - self.draw_group() - self.draw_services() - self.draw_current_services() - self.draw_buttons() - def draw_group(self): - """ - draw the group tab - - :return: nothing - """ - frame = tk.Frame(self.config_frame) - frame.columnconfigure(0, weight=1) - frame.rowconfigure(1, weight=1) - frame.grid(row=0, column=0, padx=3, pady=3, sticky="nsew") - - label = tk.Label(frame, text="Group") - label.grid(row=0, column=0, sticky="ew") - - scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL) - scrollbar.grid(row=1, column=1, sticky="ns") - - listbox = tk.Listbox( - frame, - selectmode=tk.SINGLE, - yscrollcommand=scrollbar.set, - relief=tk.FLAT, - highlightthickness=0.5, - bd=0, - ) - listbox.grid(row=1, column=0, sticky="nsew") - listbox.bind("<>", self.handle_group_change) - - for group in sorted(self.app.core.services): - listbox.insert(tk.END, group) - - scrollbar.config(command=listbox.yview) - - def draw_services(self): - frame = tk.Frame(self.config_frame) - frame.columnconfigure(0, weight=1) - frame.rowconfigure(1, weight=1) - frame.grid(row=0, column=1, padx=3, pady=3, sticky="nsew") - - label = tk.Label(frame, text="Group services") - label.grid(row=0, column=0, sticky="ew") - - scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL) - scrollbar.grid(row=1, column=1, sticky="ns") - - self.services_list = tk.Listbox( - frame, - selectmode=tk.SINGLE, - yscrollcommand=scrollbar.set, - relief=tk.FLAT, - highlightthickness=0.5, - bd=0, - ) - self.services_list.grid(row=1, column=0, sticky="nsew") - self.services_list.bind("<>", self.handle_service_change) - - scrollbar.config(command=self.services_list.yview) - - def draw_current_services(self): - frame = tk.Frame(self.config_frame) - frame.columnconfigure(0, weight=1) - frame.rowconfigure(1, weight=1) - frame.grid(row=0, column=2, padx=3, pady=3, sticky="nsew") - - label = tk.Label(frame, text="Current services") - label.grid(row=0, column=0, sticky="ew") - - scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL) - scrollbar.grid(row=1, column=1, sticky="ns") - - listbox = tk.Listbox( - frame, - selectmode=tk.MULTIPLE, - yscrollcommand=scrollbar.set, - relief=tk.FLAT, - highlightthickness=0.5, - bd=0, - ) - listbox.grid(row=1, column=0, sticky="nsew") - - scrollbar.config(command=listbox.yview) - - def draw_buttons(self): frame = tk.Frame(self) - frame.columnconfigure(0, weight=1) - frame.columnconfigure(1, weight=1) - frame.columnconfigure(2, weight=1) - frame.grid(row=1, column=0, sticky="ew") + frame.grid(stick="nsew") + frame.rowconfigure(0, weight=1) + for i in range(3): + frame.columnconfigure(i, weight=1) + self.groups = ListboxScroll(frame, text="Groups") + self.groups.grid(row=0, column=0, sticky="nsew") + for group in sorted(self.app.core.services): + self.groups.listbox.insert(tk.END, group) + self.groups.listbox.bind("<>", self.handle_group_change) + self.groups.listbox.selection_set(0) + self.services = CheckboxList( + frame, text="Services", clicked=self.service_clicked + ) + self.services.grid(row=0, column=1, sticky="nsew") + + self.current = ListboxScroll(frame, text="Selected") + self.current.grid(row=0, column=2, sticky="nsew") + for service in sorted(self.current_services): + self.current.listbox.insert(tk.END, service) + + frame = tk.Frame(self) + frame.grid(stick="ew") + for i in range(3): + frame.columnconfigure(i, weight=1) button = tk.Button(frame, text="Configure", command=self.click_configure) button.grid(row=0, column=0, sticky="ew") - - button = tk.Button(frame, text="Apply") + button = tk.Button(frame, text="Save", command=self.click_save) button.grid(row=0, column=1, sticky="ew") - - button = tk.Button(frame, text="Cancel", command=self.destroy) + button = tk.Button(frame, text="Cancel", command=self.click_cancel) button.grid(row=0, column=2, sticky="ew") + # trigger group change + self.groups.listbox.event_generate("<>") + def handle_group_change(self, event): - listbox = event.widget - cur_selection = listbox.curselection() - if cur_selection: - s = listbox.get(listbox.curselection()) - self.display_group_services(s) + selection = self.groups.listbox.curselection() + if selection: + index = selection[0] + group = self.groups.listbox.get(index) + self.services.clear() + for service in sorted(self.app.core.services[group], key=lambda x: x.name): + checked = service.name in self.current_services + self.services.add(service.name, checked) - def display_group_services(self, group_name): - self.services_list.delete(0, tk.END) - for service in sorted(self.app.core.services[group_name], key=lambda x: x.name): - self.services_list.insert(tk.END, service.name) - - def handle_service_change(self, event): - print("select group service") - listbox = event.widget - cur_selection = listbox.curselection() - if cur_selection: - s = listbox.get(listbox.curselection()) - self.service_to_config = s - else: - self.service_to_config = None + def service_clicked(self, name, var): + if var.get() and name not in self.current_services: + 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) def click_configure(self): - if self.service_to_config is None: - messagebox.showinfo("CORE info", "Choose a service to configure.") + current_selection = self.current.listbox.curselection() + if len(current_selection): + dialog = ServiceConfiguration( + master=self, + app=self.app, + service_name=self.current.listbox.get(current_selection[0]), + canvas_node=self.canvas_node, + ) + dialog.show() else: - print(self.service_to_config) + messagebox.showinfo( + "Node service configuration", "Select a service to configure" + ) + + def click_save(self): + print("not implemented") + print(self.current_services) + + def click_cancel(self): + self.current_services = None + self.destroy() diff --git a/coretk/coretk/dialogs/serviceconfiguration.py b/coretk/coretk/dialogs/serviceconfiguration.py new file mode 100644 index 00000000..e352a2e8 --- /dev/null +++ b/coretk/coretk/dialogs/serviceconfiguration.py @@ -0,0 +1,185 @@ +"Service configuration dialog" + +import tkinter as tk +from tkinter import ttk + +from coretk.dialogs.dialog import Dialog +from coretk.images import ImageEnum, Images +from coretk.widgets import ListboxScroll + + +class ServiceConfiguration(Dialog): + def __init__(self, master, app, service_name, canvas_node): + super().__init__(master, app, service_name + " service", modal=True) + self.app = app + self.service_name = service_name + self.metadata = tk.StringVar() + self.filename = tk.StringVar() + self.radiovar = tk.IntVar() + self.radiovar.set(1) + self.startup_index = tk.IntVar() + self.start_time = tk.IntVar() + self.documentnew_img = Images.get(ImageEnum.DOCUMENTNEW) + self.editdelete_img = Images.get(ImageEnum.EDITDELETE) + self.draw() + + def draw(self): + # self.columnconfigure(1, weight=1) + frame = tk.Frame(self) + frame1 = tk.Frame(frame) + label = tk.Label(frame1, text=self.service_name) + label.grid(row=0, column=0, sticky="ew") + frame1.grid(row=0, column=0) + frame2 = tk.Frame(frame) + # frame2.columnconfigure(0, weight=1) + # frame2.columnconfigure(1, weight=4) + label = tk.Label(frame2, text="Meta-data") + label.grid(row=0, column=0) + entry = tk.Entry(frame2, textvariable=self.metadata) + entry.grid(row=0, column=1) + frame2.grid(row=1, column=0) + frame.grid(row=0, column=0) + + frame = tk.Frame(self) + tab_parent = ttk.Notebook(frame) + tab1 = ttk.Frame(tab_parent) + tab2 = ttk.Frame(tab_parent) + tab3 = ttk.Frame(tab_parent) + tab1.columnconfigure(0, weight=1) + tab2.columnconfigure(0, weight=1) + tab3.columnconfigure(0, weight=1) + + tab_parent.add(tab1, text="Files", sticky="nsew") + tab_parent.add(tab2, text="Directories", sticky="nsew") + tab_parent.add(tab3, text="Startup/shutdown", sticky="nsew") + tab_parent.grid(row=0, column=0, sticky="nsew") + frame.grid(row=1, column=0, sticky="nsew") + + # tab 1 + label = tk.Label( + tab1, text="Config files and scripts that are generated for this service." + ) + label.grid(row=0, column=0, sticky="nsew") + + frame = tk.Frame(tab1) + label = tk.Label(frame, text="File name: ") + label.grid(row=0, column=0) + entry = tk.Entry(frame, textvariable=self.filename) + entry.grid(row=0, column=1) + button = tk.Button(frame, image=self.documentnew_img) + button.grid(row=0, column=2) + button = tk.Button(frame, image=self.editdelete_img) + button.grid(row=0, column=3) + frame.grid(row=1, column=0, sticky="nsew") + + frame = tk.Frame(tab1) + button = tk.Radiobutton( + frame, + variable=self.radiovar, + text="Copy this source file:", + indicatoron=True, + ) + button.grid(row=0, column=0) + entry = tk.Entry(frame, state=tk.DISABLED) + entry.grid(row=0, column=1) + button = tk.Button(frame, text="not implemented") + button.grid(row=0, column=2) + frame.grid(row=2, column=0, sticky="nsew") + + frame = tk.Frame(tab1) + button = tk.Radiobutton( + frame, + variable=self.radiovar, + text="Use text below for file contents:", + indicatoron=True, + ) + button.grid(row=0, column=0) + button = tk.Button(frame, text="not implemented") + button.grid(row=0, column=1) + button = tk.Button(frame, text="not implemented") + button.grid(row=0, column=2) + frame.grid(row=3, column=0, sticky="nsew") + + # tab 2 + label = tk.Label( + tab2, + text="Directories required by this service that are unique for each node.", + ) + label.grid(row=0, column=0, sticky="nsew") + + # tab 3 + label_frame = tk.LabelFrame(tab3, text="Startup commands") + label_frame.columnconfigure(0, weight=1) + frame = tk.Frame(label_frame) + frame.columnconfigure(0, weight=1) + entry = tk.Entry(frame, textvariable=tk.StringVar()) + entry.grid(row=0, column=0, stick="nsew") + button = tk.Button(frame, image=self.documentnew_img) + button.grid(row=0, column=1, sticky="nsew") + button = tk.Button(frame, image=self.editdelete_img) + button.grid(row=0, column=2, sticky="nsew") + frame.grid(row=0, column=0, sticky="nsew") + listbox_scroll = ListboxScroll(label_frame) + listbox_scroll.listbox.config(height=4) + listbox_scroll.grid(row=1, column=0, sticky="nsew") + label_frame.grid(row=2, column=0, sticky="nsew") + + label_frame = tk.LabelFrame(tab3, text="Shutdown commands") + label_frame.columnconfigure(0, weight=1) + frame = tk.Frame(label_frame) + frame.columnconfigure(0, weight=1) + entry = tk.Entry(frame, textvariable=tk.StringVar()) + entry.grid(row=0, column=0, sticky="nsew") + button = tk.Button(frame, image=self.documentnew_img) + button.grid(row=0, column=1, sticky="nsew") + button = tk.Button(frame, image=self.editdelete_img) + button.grid(row=0, column=2, sticky="nsew") + frame.grid(row=0, column=0, sticky="nsew") + listbox_scroll = ListboxScroll(label_frame) + listbox_scroll.listbox.config(height=4) + listbox_scroll.grid(row=1, column=0, sticky="nsew") + label_frame.grid(row=3, column=0, sticky="nsew") + + label_frame = tk.LabelFrame(tab3, text="Validate commands") + label_frame.columnconfigure(0, weight=1) + frame = tk.Frame(label_frame) + frame.columnconfigure(0, weight=1) + entry = tk.Entry(frame, textvariable=tk.StringVar()) + entry.grid(row=0, column=0, sticky="nsew") + button = tk.Button(frame, image=self.documentnew_img) + button.grid(row=0, column=1, sticky="nsew") + button = tk.Button(frame, image=self.editdelete_img) + button.grid(row=0, column=2, sticky="nsew") + frame.grid(row=0, column=0, sticky="nsew") + listbox_scroll = ListboxScroll(label_frame) + listbox_scroll.listbox.config(height=4) + listbox_scroll.grid(row=1, column=0, sticky="nsew") + label_frame.grid(row=4, column=0, sticky="nsew") + + button = tk.Button( + self, text="onle store values that have changed from their defaults" + ) + button.grid(row=2, column=0) + + frame = tk.Frame(self) + button = tk.Button(frame, text="Apply", command=self.click_apply) + button.grid(row=0, column=0, sticky="nsew") + button = tk.Button(frame, text="Dafults", command=self.click_defaults) + button.grid(row=0, column=1, sticky="nsew") + button = tk.Button(frame, text="Copy...", command=self.click_copy) + button.grid(row=0, column=2, sticky="nsew") + button = tk.Button(frame, text="Cancel", command=self.click_cancel) + button.grid(row=0, column=3, sticky="nsew") + frame.grid(row=3, column=0) + + def click_apply(self, event): + print("not implemented") + + def click_defaults(self, event): + print("not implemented") + + def click_copy(self, event): + print("not implemented") + + def click_cancel(self, event): + print("not implemented") diff --git a/coretk/coretk/servicenodeconfig.py b/coretk/coretk/servicenodeconfig.py new file mode 100644 index 00000000..486e646d --- /dev/null +++ b/coretk/coretk/servicenodeconfig.py @@ -0,0 +1,37 @@ +""" +service node configuration +""" +import logging + + +class ServiceNodeConfig: + def __init__(self, app): + self.app = app + self.configurations = {} + self.default_services = {} + + def node_default_services_configuration(self, node_id, node_model): + """ + set the default configurations for the default services of a node + + :param coretk.graph.CanvasNode canvas_node: canvas node object + :return: nothing + """ + session_id = self.app.core.session_id + client = self.app.core.client + if len(self.default_services) == 0: + response = client.get_service_defaults(session_id) + logging.info("session default services: %s", response) + for default in response.defaults: + self.default_services[default.node_type] = default.services + + self.configurations[node_id] = {} + + for default in self.default_services[node_model]: + response = client.get_node_service(session_id, node_id, default) + logging.info( + "servicenodeconfig.py get node service (%s), result: %s", + node_id, + response, + ) + self.configurations[node_id][default] = response.service