def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) label = ttk.Label(self.top, text=f"{self.service} - {self.file_name}", anchor=tk.CENTER) label.grid(sticky="ew", pady=PADY) 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) 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) frame = ttk.Frame(self.top) frame.grid(sticky="ew") for i in range(3): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Copy", command=self.click_copy) button.grid(row=0, column=0, sticky="ew", padx=PADX) button = ttk.Button(frame, text="View", command=self.click_view) button.grid(row=0, column=1, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Cancel", command=self.destroy) button.grid(row=0, column=2, sticky="ew")
def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) listbox_scroll = ListboxScroll(self.top) listbox_scroll.grid(sticky=tk.NSEW, pady=PADY) self.listbox = listbox_scroll.listbox self.listbox.bind("<<ListboxSelect>>", self.select) session = self.app.core.session for file in session.hooks: self.listbox.insert(tk.END, file) frame = ttk.Frame(self.top) frame.grid(sticky=tk.EW) for i in range(4): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Create", command=self.click_create) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) self.edit_button = ttk.Button(frame, text="Edit", state=tk.DISABLED, command=self.click_edit) self.edit_button.grid(row=0, column=1, sticky=tk.EW, padx=PADX) self.delete_button = ttk.Button(frame, text="Delete", state=tk.DISABLED, command=self.click_delete) self.delete_button.grid(row=0, column=2, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Cancel", command=lambda: self.destroy()) button.grid(row=0, column=3, sticky=tk.EW)
def draw_tab_validation(self) -> None: tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky=tk.EW) tab.columnconfigure(0, weight=1) self.notebook.add(tab, text="Validation", sticky=tk.NSEW) frame = ttk.Frame(tab) frame.grid(sticky=tk.EW, pady=PADY) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Validation Time") label.grid(row=0, column=0, sticky=tk.W, padx=PADX) self.validation_time_entry = ttk.Entry(frame) self.validation_time_entry.insert("end", self.validation_time) self.validation_time_entry.config(state=tk.DISABLED) self.validation_time_entry.grid(row=0, column=1, sticky=tk.EW, pady=PADY) label = ttk.Label(frame, text="Validation Mode") label.grid(row=1, column=0, sticky=tk.W, padx=PADX) if self.validation_mode == ServiceValidationMode.BLOCKING: mode = "BLOCKING" elif self.validation_mode == ServiceValidationMode.NON_BLOCKING: mode = "NON_BLOCKING" else: mode = "TIMER" self.validation_mode_entry = ttk.Entry( frame, textvariable=tk.StringVar(value=mode) ) self.validation_mode_entry.insert("end", mode) self.validation_mode_entry.config(state=tk.DISABLED) self.validation_mode_entry.grid(row=1, column=1, sticky=tk.EW, pady=PADY) label = ttk.Label(frame, text="Validation Period") label.grid(row=2, column=0, sticky=tk.W, padx=PADX) self.validation_period_entry = ttk.Entry( frame, state=tk.DISABLED, textvariable=self.validation_period ) self.validation_period_entry.grid(row=2, column=1, sticky=tk.EW, pady=PADY) label_frame = ttk.LabelFrame(tab, text="Executables", padding=FRAME_PAD) label_frame.grid(sticky=tk.NSEW, pady=PADY) label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) listbox_scroll = ListboxScroll(label_frame) listbox_scroll.grid(sticky=tk.NSEW) tab.rowconfigure(listbox_scroll.grid_info()["row"], weight=1) for executable in self.executables: listbox_scroll.listbox.insert("end", executable) label_frame = ttk.LabelFrame(tab, text="Dependencies", padding=FRAME_PAD) label_frame.grid(sticky=tk.NSEW, pady=PADY) label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) listbox_scroll = ListboxScroll(label_frame) listbox_scroll.grid(sticky=tk.NSEW) tab.rowconfigure(listbox_scroll.grid_info()["row"], weight=1) for dependency in self.dependencies: listbox_scroll.listbox.insert("end", dependency)
def draw_listbox(self) -> None: listbox_scroll = ListboxScroll(self.top) listbox_scroll.grid(sticky="nsew", pady=PADY) listbox_scroll.columnconfigure(0, weight=1) listbox_scroll.rowconfigure(0, weight=1) self.observers = listbox_scroll.listbox self.observers.grid(row=0, column=0, sticky="nsew") self.observers.bind("<<ListboxSelect>>", self.handle_observer_change) for name in sorted(self.app.core.custom_observers): self.observers.insert(tk.END, name)
def draw_servers(self) -> None: listbox_scroll = ListboxScroll(self.top) listbox_scroll.grid(pady=PADY, sticky=tk.NSEW) listbox_scroll.columnconfigure(0, weight=1) listbox_scroll.rowconfigure(0, weight=1) self.servers = listbox_scroll.listbox self.servers.grid(row=0, column=0, sticky=tk.NSEW) self.servers.bind("<<ListboxSelect>>", self.handle_server_change) for server in self.app.core.servers: self.servers.insert(tk.END, server)
def draw_tab_startstop(self) -> None: tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky=tk.NSEW) tab.columnconfigure(0, weight=1) for i in range(3): tab.rowconfigure(i, weight=1) self.notebook.add(tab, text="Startup/Shutdown") commands = [] # tab 3 for i in range(3): label_frame = None if i == 0: label_frame = ttk.LabelFrame(tab, text="Startup Commands", padding=FRAME_PAD) commands = self.startup_commands elif i == 1: label_frame = ttk.LabelFrame(tab, text="Shutdown Commands", padding=FRAME_PAD) commands = self.shutdown_commands elif i == 2: label_frame = ttk.LabelFrame(tab, text="Validation Commands", padding=FRAME_PAD) commands = self.validation_commands label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(1, weight=1) label_frame.grid(row=i, column=0, sticky=tk.NSEW, pady=PADY) frame = ttk.Frame(label_frame) frame.grid(row=0, column=0, sticky=tk.NSEW, pady=PADY) frame.columnconfigure(0, weight=1) entry = ttk.Entry(frame, textvariable=tk.StringVar()) entry.grid(row=0, column=0, stick="ew", padx=PADX) button = ttk.Button(frame, image=self.documentnew_img) button.bind("<Button-1>", self.add_command) button.grid(row=0, column=1, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, image=self.editdelete_img) button.grid(row=0, column=2, sticky=tk.EW) button.bind("<Button-1>", self.delete_command) listbox_scroll = ListboxScroll(label_frame) listbox_scroll.listbox.bind("<<ListboxSelect>>", self.update_entry) for command in commands: listbox_scroll.listbox.insert("end", command) listbox_scroll.listbox.config(height=4) listbox_scroll.grid(row=1, column=0, sticky=tk.NSEW) if i == 0: self.startup_commands_listbox = listbox_scroll.listbox elif i == 1: self.shutdown_commands_listbox = listbox_scroll.listbox elif i == 2: self.validate_commands_listbox = listbox_scroll.listbox
def draw_tab_startstop(self) -> None: tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky=tk.NSEW) tab.columnconfigure(0, weight=1) for i in range(3): tab.rowconfigure(i, weight=1) self.notebook.add(tab, text="Startup/Shutdown") commands = [] # tab 3 for i in range(3): label_frame = None if i == 0: label_frame = ttk.LabelFrame( tab, text="Startup Commands", padding=FRAME_PAD ) commands = self.startup_commands elif i == 1: label_frame = ttk.LabelFrame( tab, text="Shutdown Commands", padding=FRAME_PAD ) commands = self.shutdown_commands elif i == 2: label_frame = ttk.LabelFrame( tab, text="Validation Commands", padding=FRAME_PAD ) commands = self.validation_commands label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) label_frame.grid(row=i, column=0, sticky=tk.NSEW, pady=PADY) listbox_scroll = ListboxScroll(label_frame) for command in commands: listbox_scroll.listbox.insert("end", command) listbox_scroll.listbox.config(height=4) listbox_scroll.grid(sticky=tk.NSEW) if i == 0: self.startup_commands_listbox = listbox_scroll.listbox elif i == 1: self.shutdown_commands_listbox = listbox_scroll.listbox elif i == 2: self.validate_commands_listbox = listbox_scroll.listbox
class ServicesSelectDialog(Dialog): def __init__(self, master: tk.BaseWidget, app: "Application", current_services: Set[str]) -> None: super().__init__(app, "Node Services", master=master) self.groups: Optional[ListboxScroll] = None self.services: Optional[CheckboxList] = None self.current: Optional[ListboxScroll] = None self.current_services: Set[str] = current_services self.draw() def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) frame = ttk.LabelFrame(self.top) frame.grid(stick=tk.NSEW, pady=PADY) frame.rowconfigure(0, weight=1) for i in range(3): frame.columnconfigure(i, weight=1) label_frame = ttk.LabelFrame(frame, text="Groups", padding=FRAME_PAD) label_frame.grid(row=0, column=0, sticky=tk.NSEW) label_frame.rowconfigure(0, weight=1) label_frame.columnconfigure(0, weight=1) self.groups = ListboxScroll(label_frame) self.groups.grid(sticky=tk.NSEW) for group in sorted(self.app.core.services): self.groups.listbox.insert(tk.END, group) self.groups.listbox.bind("<<ListboxSelect>>", self.handle_group_change) self.groups.listbox.selection_set(0) label_frame = ttk.LabelFrame(frame, text="Services") label_frame.grid(row=0, column=1, sticky=tk.NSEW) label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) self.services = CheckboxList(label_frame, self.app, clicked=self.service_clicked, padding=FRAME_PAD) self.services.grid(sticky=tk.NSEW) label_frame = ttk.LabelFrame(frame, text="Selected", padding=FRAME_PAD) label_frame.grid(row=0, column=2, sticky=tk.NSEW) label_frame.rowconfigure(0, weight=1) label_frame.columnconfigure(0, weight=1) self.current = ListboxScroll(label_frame) self.current.grid(sticky=tk.NSEW) for service in sorted(self.current_services): self.current.listbox.insert(tk.END, service) frame = ttk.Frame(self.top) frame.grid(stick=tk.EW) for i in range(2): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Save", command=self.destroy) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Cancel", command=self.click_cancel) button.grid(row=0, column=1, sticky=tk.EW) # trigger group change self.groups.listbox.event_generate("<<ListboxSelect>>") def handle_group_change(self, event: tk.Event) -> None: selection = self.groups.listbox.curselection() if selection: index = selection[0] group = self.groups.listbox.get(index) self.services.clear() for name in sorted(self.app.core.services[group]): checked = name in self.current_services self.services.add(name, checked) def service_clicked(self, name: str, var: tk.BooleanVar) -> None: 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_cancel(self) -> None: self.current_services = None self.destroy()
class CustomNodesDialog(Dialog): def __init__(self, app: "Application") -> None: super().__init__(app, "Custom Nodes") self.edit_button: Optional[ttk.Button] = None self.delete_button: Optional[ttk.Button] = None self.nodes_list: Optional[ListboxScroll] = None self.name: tk.StringVar = tk.StringVar() self.image_button: Optional[ttk.Button] = None self.image: Optional[PhotoImage] = None self.image_file: Optional[str] = None self.services: Set[str] = set() self.selected: Optional[str] = None self.selected_index: Optional[int] = None self.draw() def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) self.draw_node_config() self.draw_node_buttons() self.draw_buttons() def draw_node_config(self) -> None: frame = ttk.LabelFrame(self.top, text="Nodes", padding=FRAME_PAD) frame.grid(sticky=tk.NSEW, pady=PADY) frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) self.nodes_list = ListboxScroll(frame) self.nodes_list.grid(row=0, column=0, sticky=tk.NSEW, padx=PADX) self.nodes_list.listbox.bind("<<ListboxSelect>>", self.handle_node_select) for name in sorted(self.app.core.custom_nodes): self.nodes_list.listbox.insert(tk.END, name) frame = ttk.Frame(frame) frame.grid(row=0, column=2, sticky=tk.NSEW) frame.columnconfigure(0, weight=1) entry = ttk.Entry(frame, textvariable=self.name) entry.grid(sticky=tk.EW, pady=PADY) self.image_button = ttk.Button(frame, text="Icon", compound=tk.LEFT, command=self.click_icon) self.image_button.grid(sticky=tk.EW, pady=PADY) button = ttk.Button(frame, text="Services", command=self.click_services) button.grid(sticky=tk.EW) def draw_node_buttons(self) -> None: frame = ttk.Frame(self.top) frame.grid(sticky=tk.EW, pady=PADY) for i in range(3): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Create", command=self.click_create) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) self.edit_button = ttk.Button(frame, text="Edit", state=tk.DISABLED, command=self.click_edit) self.edit_button.grid(row=0, column=1, sticky=tk.EW, padx=PADX) self.delete_button = ttk.Button(frame, text="Delete", state=tk.DISABLED, command=self.click_delete) self.delete_button.grid(row=0, column=2, sticky=tk.EW) def draw_buttons(self) -> None: frame = ttk.Frame(self.top) frame.grid(sticky=tk.EW) for i in range(2): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Save", command=self.click_save) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Cancel", command=self.destroy) button.grid(row=0, column=1, sticky=tk.EW) def reset_values(self) -> None: self.name.set("") self.image = None self.image_file = None self.services = set() self.image_button.config(image="") def click_icon(self) -> None: file_path = image_chooser(self, ICONS_PATH) if file_path: image = Images.create(file_path, nodeutils.ICON_SIZE) self.image = image self.image_file = file_path self.image_button.config(image=self.image) def click_services(self) -> None: dialog = ServicesSelectDialog(self, self.app, self.services) dialog.show() if dialog.current_services is not None: self.services.clear() self.services.update(dialog.current_services) def click_save(self) -> None: self.app.guiconfig.nodes.clear() for name in self.app.core.custom_nodes: node_draw = self.app.core.custom_nodes[name] custom_node = CustomNode(name, node_draw.image_file, list(node_draw.services)) self.app.guiconfig.nodes.append(custom_node) logging.info("saving custom nodes: %s", self.app.guiconfig.nodes) self.app.save_config() self.destroy() def click_create(self) -> None: name = self.name.get() if name not in self.app.core.custom_nodes: image_file = Path(self.image_file).stem custom_node = CustomNode(name, image_file, list(self.services)) node_draw = NodeDraw.from_custom(custom_node) logging.info( "created new custom node (%s), image file (%s), services: (%s)", name, image_file, self.services, ) self.app.core.custom_nodes[name] = node_draw self.nodes_list.listbox.insert(tk.END, name) self.reset_values() def click_edit(self) -> None: name = self.name.get() if self.selected: previous_name = self.selected self.selected = name node_draw = self.app.core.custom_nodes.pop(previous_name) node_draw.model = name node_draw.image_file = Path(self.image_file).stem node_draw.image = self.image node_draw.services = self.services logging.debug( "edit custom node (%s), image: (%s), services (%s)", name, self.image_file, self.services, ) self.app.core.custom_nodes[name] = node_draw self.nodes_list.listbox.delete(self.selected_index) self.nodes_list.listbox.insert(self.selected_index, name) self.nodes_list.listbox.selection_set(self.selected_index) def click_delete(self) -> None: if self.selected and self.selected in self.app.core.custom_nodes: self.nodes_list.listbox.delete(self.selected_index) del self.app.core.custom_nodes[self.selected] self.reset_values() self.nodes_list.listbox.selection_clear(0, tk.END) self.nodes_list.listbox.event_generate("<<ListboxSelect>>") def handle_node_select(self, event: tk.Event) -> None: selection = self.nodes_list.listbox.curselection() if selection: self.selected_index = selection[0] self.selected = self.nodes_list.listbox.get(self.selected_index) node_draw = self.app.core.custom_nodes[self.selected] self.name.set(node_draw.model) self.services = node_draw.services self.image = node_draw.image self.image_file = node_draw.image_file self.image_button.config(image=self.image) self.edit_button.config(state=tk.NORMAL) self.delete_button.config(state=tk.NORMAL) else: self.selected = None self.selected_index = None self.edit_button.config(state=tk.DISABLED) self.delete_button.config(state=tk.DISABLED)
class ServiceConfigDialog(Dialog): def __init__(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.node: Node = node self.service_name: str = service_name self.radiovar: tk.IntVar = tk.IntVar(value=2) self.metadata: str = "" self.filenames: List[str] = [] self.dependencies: List[str] = [] self.executables: List[str] = [] self.startup_commands: List[str] = [] self.validation_commands: List[str] = [] self.shutdown_commands: List[str] = [] self.default_startup: List[str] = [] self.default_validate: List[str] = [] self.default_shutdown: List[str] = [] self.validation_mode: Optional[ServiceValidationMode] = None self.validation_time: Optional[int] = None self.validation_period: Optional[float] = None self.directory_entry: Optional[ttk.Entry] = None self.default_directories: List[str] = [] self.temp_directories: List[str] = [] self.documentnew_img: PhotoImage = self.app.get_icon( ImageEnum.DOCUMENTNEW, ICON_SIZE) self.editdelete_img: PhotoImage = self.app.get_icon( ImageEnum.EDITDELETE, ICON_SIZE) self.notebook: Optional[ttk.Notebook] = None self.metadata_entry: Optional[ttk.Entry] = None self.filename_combobox: Optional[ttk.Combobox] = None self.dir_list: Optional[ListboxScroll] = None self.startup_commands_listbox: Optional[tk.Listbox] = None self.shutdown_commands_listbox: Optional[tk.Listbox] = None self.validate_commands_listbox: Optional[tk.Listbox] = None self.validation_time_entry: Optional[ttk.Entry] = None self.validation_mode_entry: Optional[ttk.Entry] = None self.service_file_data: Optional[CodeText] = None self.validation_period_entry: Optional[ttk.Entry] = None self.original_service_files: Dict[str, str] = {} self.default_config: Optional[NodeServiceData] = None self.temp_service_files: Dict[str, str] = {} self.modified_files: Set[str] = set() self.has_error: bool = False self.load() if not self.has_error: self.draw() def load(self) -> None: try: self.app.core.create_nodes_and_links() default_config = self.app.core.get_node_service( 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.node.service_configs.get( self.service_name) self.default_config = default_config service_config = (custom_service_config if custom_service_config else default_config) self.dependencies = service_config.dependencies[:] self.executables = service_config.executables[:] self.metadata = service_config.meta self.filenames = service_config.configs[:] self.startup_commands = service_config.startup[:] self.validation_commands = service_config.validate[:] self.shutdown_commands = service_config.shutdown[:] self.validation_mode = service_config.validation_mode self.validation_time = service_config.validation_timer 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) for x in default_config.configs } self.temp_service_files = dict(self.original_service_files) 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: self.app.show_grpc_exception("Get Node Service Error", e) self.has_error = True def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) # draw metadata frame = ttk.Frame(self.top) frame.grid(sticky=tk.EW, pady=PADY) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Meta-data") label.grid(row=0, column=0, sticky=tk.W, padx=PADX) self.metadata_entry = ttk.Entry(frame, textvariable=self.metadata) self.metadata_entry.grid(row=0, column=1, sticky=tk.EW) # draw notebook self.notebook = ttk.Notebook(self.top) self.notebook.grid(sticky=tk.NSEW, pady=PADY) self.draw_tab_files() self.draw_tab_directories() self.draw_tab_startstop() self.draw_tab_configuration() self.draw_buttons() def draw_tab_files(self) -> None: tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky=tk.NSEW) tab.columnconfigure(0, weight=1) self.notebook.add(tab, text="Files") label = ttk.Label( tab, text="Config files and scripts that are generated for this service." ) label.grid() frame = ttk.Frame(tab) frame.grid(sticky=tk.EW, pady=PADY) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="File Name") label.grid(row=0, column=0, padx=PADX, sticky=tk.W) self.filename_combobox = ttk.Combobox(frame, values=self.filenames) self.filename_combobox.bind("<<ComboboxSelected>>", self.display_service_file_data) self.filename_combobox.grid(row=0, column=1, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, image=self.documentnew_img, command=self.add_filename) button.grid(row=0, column=2, padx=PADX) button = ttk.Button(frame, image=self.editdelete_img, command=self.delete_filename) button.grid(row=0, column=3) frame = ttk.Frame(tab) frame.grid(sticky=tk.EW, pady=PADY) frame.columnconfigure(1, weight=1) button = ttk.Radiobutton( frame, variable=self.radiovar, text="Copy Source File", value=1, state=tk.DISABLED, ) button.grid(row=0, column=0, sticky=tk.W, padx=PADX) entry = ttk.Entry(frame, state=tk.DISABLED) entry.grid(row=0, column=1, sticky=tk.EW, padx=PADX) image = Images.get(ImageEnum.FILEOPEN, 16) button = ttk.Button(frame, image=image) button.image = image button.grid(row=0, column=2) frame = ttk.Frame(tab) frame.grid(sticky=tk.EW, pady=PADY) frame.columnconfigure(0, weight=1) button = ttk.Radiobutton( frame, variable=self.radiovar, text="Use text below for file contents", value=2, ) button.grid(row=0, column=0, sticky=tk.EW) image = Images.get(ImageEnum.FILEOPEN, 16) button = ttk.Button(frame, image=image) button.image = image button.grid(row=0, column=1) image = Images.get(ImageEnum.DOCUMENTSAVE, 16) button = ttk.Button(frame, image=image) button.image = image button.grid(row=0, column=2) self.service_file_data = CodeText(tab) self.service_file_data.grid(sticky=tk.NSEW) tab.rowconfigure(self.service_file_data.grid_info()["row"], weight=1) if len(self.filenames) > 0: self.filename_combobox.current(0) self.service_file_data.text.delete(1.0, "end") self.service_file_data.text.insert( "end", self.temp_service_files[self.filenames[0]]) self.service_file_data.text.bind("<FocusOut>", self.update_temp_service_file_data) def draw_tab_directories(self) -> None: tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky=tk.NSEW) tab.columnconfigure(0, weight=1) tab.rowconfigure(2, weight=1) self.notebook.add(tab, text="Directories") label = ttk.Label( tab, text= "Directories required by this service that are unique for each node.", ) label.grid(row=0, column=0, sticky=tk.EW) frame = ttk.Frame(tab, padding=FRAME_PAD) frame.columnconfigure(0, weight=1) frame.grid(row=1, column=0, sticky=tk.NSEW) var = tk.StringVar(value="") self.directory_entry = ttk.Entry(frame, textvariable=var) self.directory_entry.grid(row=0, column=0, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="...", command=self.find_directory_button) button.grid(row=0, column=1, sticky=tk.EW) self.dir_list = ListboxScroll(tab) self.dir_list.grid(row=2, column=0, sticky=tk.NSEW, pady=PADY) self.dir_list.listbox.bind("<<ListboxSelect>>", self.directory_select) for d in self.temp_directories: self.dir_list.listbox.insert("end", d) frame = ttk.Frame(tab) frame.grid(row=3, column=0, sticky=tk.NSEW) frame.columnconfigure(0, weight=1) frame.columnconfigure(1, weight=1) button = ttk.Button(frame, text="Add", command=self.add_directory) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Remove", command=self.remove_directory) button.grid(row=0, column=1, sticky=tk.EW) def draw_tab_startstop(self) -> None: tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky=tk.NSEW) tab.columnconfigure(0, weight=1) for i in range(3): tab.rowconfigure(i, weight=1) self.notebook.add(tab, text="Startup/Shutdown") commands = [] # tab 3 for i in range(3): label_frame = None if i == 0: label_frame = ttk.LabelFrame(tab, text="Startup Commands", padding=FRAME_PAD) commands = self.startup_commands elif i == 1: label_frame = ttk.LabelFrame(tab, text="Shutdown Commands", padding=FRAME_PAD) commands = self.shutdown_commands elif i == 2: label_frame = ttk.LabelFrame(tab, text="Validation Commands", padding=FRAME_PAD) commands = self.validation_commands label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(1, weight=1) label_frame.grid(row=i, column=0, sticky=tk.NSEW, pady=PADY) frame = ttk.Frame(label_frame) frame.grid(row=0, column=0, sticky=tk.NSEW, pady=PADY) frame.columnconfigure(0, weight=1) entry = ttk.Entry(frame, textvariable=tk.StringVar()) entry.grid(row=0, column=0, stick="ew", padx=PADX) button = ttk.Button(frame, image=self.documentnew_img) button.bind("<Button-1>", self.add_command) button.grid(row=0, column=1, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, image=self.editdelete_img) button.grid(row=0, column=2, sticky=tk.EW) button.bind("<Button-1>", self.delete_command) listbox_scroll = ListboxScroll(label_frame) listbox_scroll.listbox.bind("<<ListboxSelect>>", self.update_entry) for command in commands: listbox_scroll.listbox.insert("end", command) listbox_scroll.listbox.config(height=4) listbox_scroll.grid(row=1, column=0, sticky=tk.NSEW) if i == 0: self.startup_commands_listbox = listbox_scroll.listbox elif i == 1: self.shutdown_commands_listbox = listbox_scroll.listbox elif i == 2: self.validate_commands_listbox = listbox_scroll.listbox def draw_tab_configuration(self) -> None: tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky=tk.NSEW) tab.columnconfigure(0, weight=1) self.notebook.add(tab, text="Configuration", sticky=tk.NSEW) frame = ttk.Frame(tab) frame.grid(sticky=tk.EW, pady=PADY) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Validation Time") label.grid(row=0, column=0, sticky=tk.W, padx=PADX) self.validation_time_entry = ttk.Entry(frame) self.validation_time_entry.insert("end", self.validation_time) self.validation_time_entry.config(state=tk.DISABLED) self.validation_time_entry.grid(row=0, column=1, sticky=tk.EW, pady=PADY) label = ttk.Label(frame, text="Validation Mode") label.grid(row=1, column=0, sticky=tk.W, padx=PADX) if self.validation_mode == ServiceValidationMode.BLOCKING: mode = "BLOCKING" elif self.validation_mode == ServiceValidationMode.NON_BLOCKING: mode = "NON_BLOCKING" else: mode = "TIMER" self.validation_mode_entry = ttk.Entry( frame, textvariable=tk.StringVar(value=mode)) self.validation_mode_entry.insert("end", mode) self.validation_mode_entry.config(state=tk.DISABLED) self.validation_mode_entry.grid(row=1, column=1, sticky=tk.EW, pady=PADY) label = ttk.Label(frame, text="Validation Period") label.grid(row=2, column=0, sticky=tk.W, padx=PADX) self.validation_period_entry = ttk.Entry(frame, state=tk.DISABLED, textvariable=tk.StringVar()) self.validation_period_entry.grid(row=2, column=1, sticky=tk.EW, pady=PADY) label_frame = ttk.LabelFrame(tab, text="Executables", padding=FRAME_PAD) label_frame.grid(sticky=tk.NSEW, pady=PADY) label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) listbox_scroll = ListboxScroll(label_frame) listbox_scroll.grid(sticky=tk.NSEW) tab.rowconfigure(listbox_scroll.grid_info()["row"], weight=1) for executable in self.executables: listbox_scroll.listbox.insert("end", executable) label_frame = ttk.LabelFrame(tab, text="Dependencies", padding=FRAME_PAD) label_frame.grid(sticky=tk.NSEW, pady=PADY) label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) listbox_scroll = ListboxScroll(label_frame) listbox_scroll.grid(sticky=tk.NSEW) tab.rowconfigure(listbox_scroll.grid_info()["row"], weight=1) for dependency in self.dependencies: listbox_scroll.listbox.insert("end", dependency) def draw_buttons(self) -> None: frame = ttk.Frame(self.top) frame.grid(sticky=tk.EW) for i in range(4): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Apply", command=self.click_apply) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Defaults", command=self.click_defaults) button.grid(row=0, column=1, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Copy...", command=self.click_copy) button.grid(row=0, column=2, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Cancel", command=self.destroy) button.grid(row=0, column=3, sticky=tk.EW) def add_filename(self) -> None: filename = self.filename_combobox.get() if filename not in self.filename_combobox["values"]: self.filename_combobox["values"] += (filename, ) self.filename_combobox.set(filename) self.temp_service_files[ filename] = self.service_file_data.text.get(1.0, "end") else: logging.debug("file already existed") def delete_filename(self) -> None: cbb = self.filename_combobox filename = cbb.get() if filename in cbb["values"]: cbb["values"] = tuple([x for x in cbb["values"] if x != filename]) cbb.set("") self.service_file_data.text.delete(1.0, "end") self.temp_service_files.pop(filename, None) if filename in self.modified_files: self.modified_files.remove(filename) @classmethod def add_command(cls, event: tk.Event) -> None: frame_contains_button = event.widget.master listbox = frame_contains_button.master.grid_slaves(row=1, column=0)[0].listbox command_to_add = frame_contains_button.grid_slaves(row=0, column=0)[0].get() if command_to_add == "": return for cmd in listbox.get(0, tk.END): if cmd == command_to_add: return listbox.insert(tk.END, command_to_add) @classmethod def update_entry(cls, event: tk.Event) -> None: listbox = event.widget current_selection = listbox.curselection() if len(current_selection) > 0: cmd = listbox.get(current_selection[0]) entry = listbox.master.master.grid_slaves( row=0, column=0)[0].grid_slaves(row=0, column=0)[0] entry.delete(0, "end") entry.insert(0, cmd) @classmethod def delete_command(cls, event: tk.Event) -> None: button = event.widget frame_contains_button = button.master listbox = frame_contains_button.master.grid_slaves(row=1, column=0)[0].listbox current_selection = listbox.curselection() if len(current_selection) > 0: listbox.delete(current_selection[0]) entry = frame_contains_button.grid_slaves(row=0, column=0)[0] entry.delete(0, tk.END) def click_apply(self) -> None: if (not self.is_custom_command() and not self.is_custom_service_file() and not self.has_new_files() and not self.is_custom_directory()): self.node.service_configs.pop(self.service_name, None) self.current_service_color("") self.destroy() return try: if (self.is_custom_command() or self.has_new_files() or self.is_custom_directory()): startup, validate, shutdown = self.get_commands() config = self.core.set_node_service( self.node.id, self.service_name, dirs=self.temp_directories, files=list(self.filename_combobox["values"]), startups=startup, validations=validate, shutdowns=shutdown, ) self.node.service_configs[self.service_name] = config for file in self.modified_files: 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.current_service_color("green") except grpc.RpcError as e: self.app.show_grpc_exception("Save Service Config Error", e) self.destroy() def display_service_file_data(self, event: tk.Event) -> None: filename = self.filename_combobox.get() self.service_file_data.text.delete(1.0, "end") self.service_file_data.text.insert("end", self.temp_service_files[filename]) def update_temp_service_file_data(self, event: tk.Event) -> None: filename = self.filename_combobox.get() self.temp_service_files[filename] = self.service_file_data.text.get( 1.0, "end") if self.temp_service_files[ filename] != self.original_service_files.get(filename, ""): self.modified_files.add(filename) else: self.modified_files.discard(filename) def is_custom_command(self) -> bool: startup, validate, shutdown = self.get_commands() return (set(self.default_startup) != set(startup) or set(self.default_validate) != set(validate) or set(self.default_shutdown) != set(shutdown)) def has_new_files(self) -> bool: return set(self.filenames) != set(self.filename_combobox["values"]) def is_custom_service_file(self) -> bool: return len(self.modified_files) > 0 def is_custom_directory(self) -> bool: return set(self.default_directories) != set( self.dir_list.listbox.get(0, "end")) def click_defaults(self) -> None: """ clears out any custom configuration permanently """ # clear coreclient data 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() # reset files tab files = list(self.default_config.configs[:]) self.filenames = files self.filename_combobox.config(values=files) self.service_file_data.text.delete(1.0, "end") if len(files) > 0: filename = files[0] self.filename_combobox.set(filename) self.service_file_data.text.insert( "end", self.temp_service_files[filename]) # reset commands self.startup_commands_listbox.delete(0, tk.END) self.validate_commands_listbox.delete(0, tk.END) self.shutdown_commands_listbox.delete(0, tk.END) for cmd in self.default_startup: self.startup_commands_listbox.insert(tk.END, cmd) for cmd in self.default_validate: self.validate_commands_listbox.insert(tk.END, cmd) for cmd in self.default_shutdown: self.shutdown_commands_listbox.insert(tk.END, cmd) # reset directories self.directory_entry.delete(0, "end") self.dir_list.listbox.delete(0, "end") self.temp_directories = list(self.default_directories) for d in self.default_directories: self.dir_list.listbox.insert("end", d) self.current_service_color("") def click_copy(self) -> None: file_name = self.filename_combobox.get() dialog = CopyServiceConfigDialog(self.app, self, self.node.name, self.service_name, file_name) dialog.show() @classmethod def append_commands(cls, commands: List[str], listbox: tk.Listbox, to_add: List[str]) -> None: for cmd in to_add: commands.append(cmd) listbox.insert(tk.END, cmd) def get_commands(self) -> Tuple[List[str], List[str], List[str]]: startup = self.startup_commands_listbox.get(0, "end") shutdown = self.shutdown_commands_listbox.get(0, "end") validate = self.validate_commands_listbox.get(0, "end") return startup, validate, shutdown def find_directory_button(self) -> None: d = filedialog.askdirectory(initialdir="/") self.directory_entry.delete(0, "end") self.directory_entry.insert("end", d) def add_directory(self) -> None: d = self.directory_entry.get() if os.path.isdir(d): if d not in self.temp_directories: self.dir_list.listbox.insert("end", d) self.temp_directories.append(d) def remove_directory(self) -> None: d = self.directory_entry.get() dirs = self.dir_list.listbox.get(0, "end") if d and d in self.temp_directories: self.temp_directories.remove(d) try: i = dirs.index(d) self.dir_list.listbox.delete(i) except ValueError: logging.debug("directory is not in the list") self.directory_entry.delete(0, "end") def directory_select(self, event) -> None: i = self.dir_list.listbox.curselection() if i: d = self.dir_list.listbox.get(i) self.directory_entry.delete(0, "end") self.directory_entry.insert("end", d) def current_service_color(self, color="") -> None: """ change the current service label color """ listbox = self.master.current.listbox services = listbox.get(0, tk.END) listbox.itemconfig(services.index(self.service_name), bg=color)
def draw(self) -> None: self.top.columnconfigure(0, weight=1) row = 0 # field states state = tk.DISABLED if self.app.core.is_runtime() else tk.NORMAL combo_state = tk.DISABLED if self.app.core.is_runtime() else "readonly" # field frame frame = ttk.Frame(self.top) frame.grid(sticky=tk.EW) frame.columnconfigure(1, weight=1) # icon field label = ttk.Label(frame, text="Icon") label.grid(row=row, column=0, sticky=tk.EW, padx=PADX, pady=PADY) self.image_button = ttk.Button( frame, text="Icon", image=self.image, compound=tk.NONE, command=self.click_icon, ) self.image_button.grid(row=row, column=1, sticky=tk.EW) row += 1 # name field label = ttk.Label(frame, text="Name") label.grid(row=row, column=0, sticky=tk.EW, padx=PADX, pady=PADY) entry = validation.NodeNameEntry(frame, textvariable=self.name, state=state) entry.grid(row=row, column=1, sticky=tk.EW) row += 1 # node type field if NodeUtils.is_model_node(self.node.type): label = ttk.Label(frame, text="Type") label.grid(row=row, column=0, sticky=tk.EW, padx=PADX, pady=PADY) combobox = ttk.Combobox( frame, textvariable=self.type, values=list(NodeUtils.NODE_MODELS), state=combo_state, ) combobox.grid(row=row, column=1, sticky=tk.EW) row += 1 # container image field if NodeUtils.is_image_node(self.node.type): label = ttk.Label(frame, text="Image") label.grid(row=row, column=0, sticky=tk.EW, padx=PADX, pady=PADY) entry = ttk.Entry(frame, textvariable=self.container_image, state=state) entry.grid(row=row, column=1, sticky=tk.EW) row += 1 if NodeUtils.is_container_node(self.node.type): # server frame.grid(sticky=tk.EW) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Server") label.grid(row=row, column=0, sticky=tk.EW, padx=PADX, pady=PADY) servers = ["localhost"] servers.extend(list(sorted(self.app.core.servers.keys()))) combobox = ttk.Combobox(frame, textvariable=self.server, values=servers, state=combo_state) combobox.grid(row=row, column=1, sticky=tk.EW) row += 1 if NodeUtils.is_rj45_node(self.node.type): response = self.app.core.client.get_ifaces() logging.debug("host machine available interfaces: %s", response) ifaces = ListboxScroll(frame) ifaces.listbox.config(state=state) ifaces.grid(row=row, column=0, columnspan=2, sticky=tk.EW, padx=PADX, pady=PADY) for inf in sorted(response.ifaces[:]): ifaces.listbox.insert(tk.END, inf) row += 1 ifaces.listbox.bind("<<ListboxSelect>>", self.iface_select) # interfaces if self.canvas_node.ifaces: self.draw_ifaces() self.draw_spacer() self.draw_buttons()
def draw(self): self.top.columnconfigure(0, weight=1) row = 0 # field frame frame = ttk.Frame(self.top) frame.grid(sticky="ew") frame.columnconfigure(1, weight=1) # icon field label = ttk.Label(frame, text="Icon") label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY) self.image_button = ttk.Button( frame, text="Icon", image=self.image, compound=tk.NONE, command=self.click_icon, ) self.image_button.grid(row=row, column=1, sticky="ew") row += 1 # name field label = ttk.Label(frame, text="Name") label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY) entry = ttk.Entry( frame, textvariable=self.name, validate="key", validatecommand=(self.app.validation.name, "%P"), ) entry.bind( "<FocusOut>", lambda event: self.app.validation.focus_out(event, "noname")) entry.grid(row=row, column=1, sticky="ew") row += 1 # node type field if NodeUtils.is_model_node(self.node.type): label = ttk.Label(frame, text="Type") label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY) combobox = ttk.Combobox( frame, textvariable=self.type, values=list(NodeUtils.NODE_MODELS), state="readonly", ) combobox.grid(row=row, column=1, sticky="ew") row += 1 # container image field if NodeUtils.is_image_node(self.node.type): label = ttk.Label(frame, text="Image") label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY) entry = ttk.Entry(frame, textvariable=self.container_image) entry.grid(row=row, column=1, sticky="ew") row += 1 if NodeUtils.is_container_node(self.node.type): # server frame.grid(sticky="ew") frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Server") label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY) servers = ["localhost"] servers.extend(list(sorted(self.app.core.servers.keys()))) combobox = ttk.Combobox(frame, textvariable=self.server, values=servers, state="readonly") combobox.grid(row=row, column=1, sticky="ew") row += 1 if NodeUtils.is_rj45_node(self.node.type): response = self.app.core.client.get_interfaces() logging.debug("host machine available interfaces: %s", response) interfaces = ListboxScroll(frame) interfaces.grid(row=row, column=0, columnspan=2, sticky="ew", padx=PADX, pady=PADY) for inf in sorted(response.interfaces[:]): interfaces.listbox.insert(tk.END, inf) row += 1 interfaces.listbox.bind("<<ListboxSelect>>", self.interface_select) # interfaces if self.canvas_node.interfaces: self.draw_interfaces() self.draw_spacer() self.draw_buttons()
class NodeServiceDialog(Dialog): def __init__(self, app: "Application", node: Node) -> None: title = f"{node.name} Services (Deprecated)" super().__init__(app, title) self.node: Node = node self.groups: Optional[ListboxScroll] = None self.services: Optional[CheckboxList] = None self.current: Optional[ListboxScroll] = None services = set(node.services) self.current_services: Set[str] = services self.protocol("WM_DELETE_WINDOW", self.click_cancel) self.draw() def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) frame = ttk.Frame(self.top) frame.grid(stick="nsew", pady=PADY) frame.rowconfigure(0, weight=1) for i in range(3): frame.columnconfigure(i, weight=1) label_frame = ttk.LabelFrame(frame, text="Groups", padding=FRAME_PAD) label_frame.grid(row=0, column=0, sticky=tk.NSEW) label_frame.rowconfigure(0, weight=1) label_frame.columnconfigure(0, weight=1) self.groups = ListboxScroll(label_frame) self.groups.grid(sticky=tk.NSEW) for group in sorted(self.app.core.services): self.groups.listbox.insert(tk.END, group) self.groups.listbox.bind("<<ListboxSelect>>", self.handle_group_change) self.groups.listbox.selection_set(0) label_frame = ttk.LabelFrame(frame, text="Services") label_frame.grid(row=0, column=1, sticky=tk.NSEW) label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) self.services = CheckboxList(label_frame, self.app, clicked=self.service_clicked, padding=FRAME_PAD) self.services.grid(sticky=tk.NSEW) label_frame = ttk.LabelFrame(frame, text="Selected", padding=FRAME_PAD) label_frame.grid(row=0, column=2, sticky=tk.NSEW) label_frame.rowconfigure(0, weight=1) label_frame.columnconfigure(0, weight=1) self.current = ListboxScroll(label_frame) self.current.grid(sticky=tk.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") frame = ttk.Frame(self.top) frame.grid(stick="ew") for i in range(4): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Configure", command=self.click_configure) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Save", command=self.click_save) button.grid(row=0, column=1, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Remove", command=self.click_remove) button.grid(row=0, column=2, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Cancel", command=self.click_cancel) button.grid(row=0, column=3, sticky=tk.EW) # trigger group change self.handle_group_change() def handle_group_change(self, event: tk.Event = None) -> None: selection = self.groups.listbox.curselection() if selection: index = selection[0] group = self.groups.listbox.get(index) self.services.clear() for name in sorted(self.app.core.services[group]): checked = name in self.current_services self.services.add(name, checked) def service_clicked(self, name: str, var: tk.IntVar) -> None: 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.node.service_configs.pop(name, None) self.node.service_file_configs.pop(name, None) 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.node.services = self.current_services.copy() def click_configure(self) -> None: current_selection = self.current.listbox.curselection() if len(current_selection): dialog = ServiceConfigDialog( self, self.app, self.current.listbox.get(current_selection[0]), self.node, ) # if error occurred when creating ServiceConfigDialog, don't show the dialog if not dialog.has_error: dialog.show() else: dialog.destroy() else: messagebox.showinfo("Service Configuration", "Select a service to configure", parent=self) def click_cancel(self) -> None: self.destroy() def click_save(self) -> None: self.node.services = self.current_services.copy() self.destroy() def click_remove(self) -> None: cur = self.current.listbox.curselection() if cur: service = self.current.listbox.get(cur[0]) self.current.listbox.delete(cur[0]) self.current_services.remove(service) self.node.service_configs.pop(service, None) self.node.service_file_configs.pop(service, None) for checkbutton in self.services.frame.winfo_children(): if checkbutton["text"] == service: checkbutton.invoke() return def is_custom_service(self, service: str) -> bool: 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
class RunToolDialog(Dialog): def __init__(self, app: "Application") -> None: super().__init__(app, "Run Tool") self.cmd: tk.StringVar = tk.StringVar(value="ps ax") self.result: Optional[CodeText] = None self.node_list: Optional[ListboxScroll] = None self.executable_nodes: Dict[str, int] = {} self.store_nodes() self.draw() def store_nodes(self) -> None: """ store all CORE nodes (nodes that execute commands) from all existing nodes """ for node in self.app.core.session.nodes.values(): if NodeUtils.is_container_node(node.type): self.executable_nodes[node.name] = node.id def draw(self) -> None: self.top.rowconfigure(0, weight=1) self.top.columnconfigure(0, weight=1) self.draw_command_frame() self.draw_nodes_frame() def draw_command_frame(self) -> None: # the main frame frame = ttk.Frame(self.top) frame.grid(row=0, column=0, sticky=tk.NSEW, padx=PADX) frame.columnconfigure(0, weight=1) frame.rowconfigure(1, weight=1) labeled_frame = ttk.LabelFrame(frame, text="Command", padding=FRAME_PAD) labeled_frame.grid(sticky=tk.EW, pady=PADY) labeled_frame.rowconfigure(0, weight=1) labeled_frame.columnconfigure(0, weight=1) entry = ttk.Entry(labeled_frame, textvariable=self.cmd) entry.grid(sticky=tk.EW) # results frame labeled_frame = ttk.LabelFrame(frame, text="Output", padding=FRAME_PAD) labeled_frame.grid(sticky=tk.NSEW, pady=PADY) labeled_frame.columnconfigure(0, weight=1) labeled_frame.rowconfigure(0, weight=1) self.result = CodeText(labeled_frame) self.result.text.config(state=tk.DISABLED, height=15) self.result.grid(sticky=tk.NSEW, pady=PADY) button_frame = ttk.Frame(labeled_frame) button_frame.grid(sticky=tk.NSEW) button_frame.columnconfigure(0, weight=1) button_frame.columnconfigure(1, weight=1) button = ttk.Button(button_frame, text="Run", command=self.click_run) button.grid(sticky=tk.EW, padx=PADX) button = ttk.Button(button_frame, text="Close", command=self.destroy) button.grid(row=0, column=1, sticky=tk.EW) def draw_nodes_frame(self) -> None: labeled_frame = ttk.LabelFrame(self.top, text="Nodes", padding=FRAME_PAD) labeled_frame.grid(row=0, column=1, sticky=tk.NSEW) labeled_frame.columnconfigure(0, weight=1) labeled_frame.rowconfigure(0, weight=1) self.node_list = ListboxScroll(labeled_frame) self.node_list.listbox.config(selectmode=tk.MULTIPLE) self.node_list.grid(sticky=tk.NSEW, pady=PADY) for n in sorted(self.executable_nodes.keys()): self.node_list.listbox.insert(tk.END, n) button_frame = ttk.Frame(labeled_frame, padding=FRAME_PAD) button_frame.grid(sticky=tk.NSEW) button_frame.columnconfigure(0, weight=1) button_frame.columnconfigure(1, weight=1) button = ttk.Button(button_frame, text="All", command=self.click_all) button.grid(sticky=tk.NSEW, padx=PADX) button = ttk.Button(button_frame, text="None", command=self.click_none) button.grid(row=0, column=1, sticky=tk.NSEW) def click_all(self) -> None: self.node_list.listbox.selection_set(0, self.node_list.listbox.size() - 1) def click_none(self) -> None: self.node_list.listbox.selection_clear(0, self.node_list.listbox.size() - 1) def click_run(self) -> None: """ Run the command on each of the selected nodes and display the output to result text box. """ command = self.cmd.get().strip() self.result.text.config(state=tk.NORMAL) self.result.text.delete("1.0", tk.END) for selection in self.node_list.listbox.curselection(): 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.result.text.insert( tk.END, f"> {node_name} > {command}:\n{response.output}\n" ) self.result.text.config(state=tk.DISABLED)
class NodeServiceDialog(Dialog): def __init__( self, master: Any, app: "Application", canvas_node: "CanvasNode", services: Set[str] = None, ): title = f"{canvas_node.core_node.name} Services" super().__init__(master, app, title, modal=True) self.app = app self.canvas_node = canvas_node self.node_id = canvas_node.core_node.id self.groups = None self.services = None self.current = None if services is None: services = canvas_node.core_node.services model = canvas_node.core_node.model if len(services) == 0: # not custom node type and node's services haven't been modified before if not NodeUtils.is_custom( canvas_node.core_node.model ) and not self.app.core.service_been_modified(self.node_id): services = set(self.app.core.default_services[model]) # services of default type nodes were modified to be empty elif canvas_node.core_node.id in self.app.core.modified_service_nodes: services = set() else: services = set( NodeUtils.get_custom_node_services( self.app.guiconfig, model)) else: services = set(services) self.current_services = services self.draw() def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) frame = ttk.Frame(self.top) frame.grid(stick="nsew", pady=PADY) frame.rowconfigure(0, weight=1) for i in range(3): frame.columnconfigure(i, weight=1) label_frame = ttk.LabelFrame(frame, text="Groups", padding=FRAME_PAD) label_frame.grid(row=0, column=0, sticky="nsew") label_frame.rowconfigure(0, weight=1) label_frame.columnconfigure(0, weight=1) self.groups = ListboxScroll(label_frame) self.groups.grid(sticky="nsew") for group in sorted(self.app.core.services): self.groups.listbox.insert(tk.END, group) self.groups.listbox.bind("<<ListboxSelect>>", self.handle_group_change) self.groups.listbox.selection_set(0) label_frame = ttk.LabelFrame(frame, text="Services") label_frame.grid(row=0, column=1, sticky="nsew") label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) self.services = CheckboxList(label_frame, self.app, clicked=self.service_clicked, padding=FRAME_PAD) self.services.grid(sticky="nsew") label_frame = ttk.LabelFrame(frame, text="Selected", padding=FRAME_PAD) 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") frame = ttk.Frame(self.top) frame.grid(stick="ew") for i in range(4): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Configure", command=self.click_configure) button.grid(row=0, column=0, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Save", command=self.click_save) button.grid(row=0, column=1, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Remove", command=self.click_remove) button.grid(row=0, column=2, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Cancel", command=self.click_cancel) button.grid(row=0, column=3, sticky="ew") # trigger group change self.groups.listbox.event_generate("<<ListboxSelect>>") def handle_group_change(self, event: tk.Event = None): selection = self.groups.listbox.curselection() if selection: index = selection[0] group = self.groups.listbox.get(index) self.services.clear() for name in sorted(self.app.core.services[group]): checked = name in self.current_services self.services.add(name, checked) def service_clicked(self, name: str, var: tk.IntVar): 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) if self.is_custom_service(name): self.current.listbox.itemconfig(tk.END, bg="green") self.canvas_node.core_node.services[:] = self.current_services def click_configure(self): current_selection = self.current.listbox.curselection() if len(current_selection): dialog = ServiceConfigDialog( master=self, app=self.app, service_name=self.current.listbox.get(current_selection[0]), node_id=self.node_id, ) # if error occurred when creating ServiceConfigDialog, don't show the dialog if not dialog.has_error: dialog.show() else: dialog.destroy() else: messagebox.showinfo("Node service configuration", "Select a service to configure") def click_save(self): # if node is custom type or current services are not the default services then set core node services and add node to modified services node set if (self.canvas_node.core_node.model not in self.app.core.default_services or self.current_services != self.app.core.default_services[ self.canvas_node.core_node.model]): self.canvas_node.core_node.services[:] = self.current_services self.app.core.modified_service_nodes.add( self.canvas_node.core_node.id) else: if len(self.canvas_node.core_node.services) > 0: self.canvas_node.core_node.services[:] = [] self.destroy() def click_cancel(self): self.current_services = None self.destroy() def click_remove(self): cur = self.current.listbox.curselection() if cur: service = self.current.listbox.get(cur[0]) self.current.listbox.delete(cur[0]) self.current_services.remove(service) for checkbutton in self.services.frame.winfo_children(): if checkbutton["text"] == service: checkbutton.invoke() return def is_custom_service(self, service: str) -> bool: service_configs = self.app.core.service_configs file_configs = self.app.core.file_configs if self.node_id in service_configs and service in service_configs[ self.node_id]: return True if (self.node_id in file_configs and service in file_configs[self.node_id] and file_configs[self.node_id][service]): return True return False
class IpConfigDialog(Dialog): def __init__(self, app: "Application") -> None: super().__init__(app, "IP Configuration") self.ip4 = self.app.guiconfig.ips.ip4 self.ip6 = self.app.guiconfig.ips.ip6 self.ip4s = self.app.guiconfig.ips.ip4s self.ip6s = self.app.guiconfig.ips.ip6s self.ip4_entry = None self.ip4_listbox = None self.ip6_entry = None self.ip6_listbox = None self.draw() def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) # draw ip4 and ip6 lists frame = ttk.Frame(self.top) frame.columnconfigure(0, weight=1) frame.columnconfigure(1, weight=1) frame.rowconfigure(0, weight=1) frame.grid(sticky="nsew", pady=PADY) ip4_frame = ttk.LabelFrame(frame, text="IPv4", padding=FRAME_PAD) ip4_frame.columnconfigure(0, weight=1) ip4_frame.rowconfigure(0, weight=1) ip4_frame.grid(row=0, column=0, stick="nsew") self.ip4_listbox = ListboxScroll(ip4_frame) self.ip4_listbox.listbox.bind("<<ListboxSelect>>", self.select_ip4) self.ip4_listbox.grid(sticky="nsew", pady=PADY) for index, ip4 in enumerate(self.ip4s): self.ip4_listbox.listbox.insert(tk.END, ip4) if self.ip4 == ip4: self.ip4_listbox.listbox.select_set(index) self.ip4_entry = ttk.Entry(ip4_frame) self.ip4_entry.grid(sticky="ew", pady=PADY) ip4_button_frame = ttk.Frame(ip4_frame) ip4_button_frame.columnconfigure(0, weight=1) ip4_button_frame.columnconfigure(1, weight=1) ip4_button_frame.grid(sticky="ew") ip4_add = ttk.Button(ip4_button_frame, text="Add", command=self.click_add_ip4) ip4_add.grid(row=0, column=0, sticky="ew") ip4_del = ttk.Button(ip4_button_frame, text="Delete", command=self.click_del_ip4) ip4_del.grid(row=0, column=1, sticky="ew") ip6_frame = ttk.LabelFrame(frame, text="IPv6", padding=FRAME_PAD) ip6_frame.columnconfigure(0, weight=1) ip6_frame.rowconfigure(0, weight=1) ip6_frame.grid(row=0, column=1, stick="nsew") self.ip6_listbox = ListboxScroll(ip6_frame) self.ip6_listbox.listbox.bind("<<ListboxSelect>>", self.select_ip6) self.ip6_listbox.grid(sticky="nsew", pady=PADY) for index, ip6 in enumerate(self.ip6s): self.ip6_listbox.listbox.insert(tk.END, ip6) if self.ip6 == ip6: self.ip6_listbox.listbox.select_set(index) self.ip6_entry = ttk.Entry(ip6_frame) self.ip6_entry.grid(sticky="ew", pady=PADY) ip6_button_frame = ttk.Frame(ip6_frame) ip6_button_frame.columnconfigure(0, weight=1) ip6_button_frame.columnconfigure(1, weight=1) ip6_button_frame.grid(sticky="ew") ip6_add = ttk.Button(ip6_button_frame, text="Add", command=self.click_add_ip6) ip6_add.grid(row=0, column=0, sticky="ew") ip6_del = ttk.Button(ip6_button_frame, text="Delete", command=self.click_del_ip6) ip6_del.grid(row=0, column=1, sticky="ew") # draw buttons frame = ttk.Frame(self.top) frame.grid(stick="ew") for i in range(2): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Save", command=self.click_save) button.grid(row=0, column=0, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Cancel", command=self.destroy) button.grid(row=0, column=1, sticky="ew") def click_add_ip4(self) -> None: ip4 = self.ip4_entry.get() if not ip4 or not netaddr.valid_ipv4(ip4): messagebox.showerror("IPv4 Error", f"Invalid IPv4 {ip4}") else: self.ip4_listbox.listbox.insert(tk.END, ip4) def click_del_ip4(self) -> None: if self.ip4_listbox.listbox.size() == 1: messagebox.showerror("IPv4 Error", "Must have at least one address") else: selection = self.ip4_listbox.listbox.curselection() self.ip4_listbox.listbox.delete(selection) self.ip4_listbox.listbox.select_set(0) def click_add_ip6(self) -> None: ip6 = self.ip6_entry.get() if not ip6 or not netaddr.valid_ipv6(ip6): messagebox.showerror("IPv6 Error", f"Invalid IPv6 {ip6}") else: self.ip6_listbox.listbox.insert(tk.END, ip6) def click_del_ip6(self) -> None: if self.ip6_listbox.listbox.size() == 1: messagebox.showerror("IPv6 Error", "Must have at least one address") else: selection = self.ip6_listbox.listbox.curselection() self.ip6_listbox.listbox.delete(selection) self.ip6_listbox.listbox.select_set(0) def select_ip4(self, _event: tk.Event) -> None: selection = self.ip4_listbox.listbox.curselection() self.ip4 = self.ip4_listbox.listbox.get(selection) def select_ip6(self, _event: tk.Event) -> None: selection = self.ip6_listbox.listbox.curselection() self.ip6 = self.ip6_listbox.listbox.get(selection) def click_save(self) -> None: ip4s = [] for index in range(self.ip4_listbox.listbox.size()): ip4 = self.ip4_listbox.listbox.get(index) ip4s.append(ip4) ip6s = [] for index in range(self.ip6_listbox.listbox.size()): ip6 = self.ip6_listbox.listbox.get(index) ip6s.append(ip6) ip_config = self.app.guiconfig.ips ip_config.ip4 = self.ip4 ip_config.ip6 = self.ip6 ip_config.ip4s = ip4s ip_config.ip6s = ip6s self.app.core.interfaces_manager.update_ips(self.ip4, self.ip6) self.app.save_config() self.destroy()
class NodeConfigServiceDialog(Dialog): def __init__( self, master: Any, app: "Application", canvas_node: "CanvasNode", services: Set[str] = None, ): title = f"{canvas_node.core_node.name} Config Services" super().__init__(master, app, title, modal=True) self.app = app self.canvas_node = canvas_node self.node_id = canvas_node.core_node.id self.groups = None self.services = None self.current = None if services is None: services = set(canvas_node.core_node.config_services) self.current_services = services self.draw() def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) frame = ttk.Frame(self.top) frame.grid(stick="nsew", pady=PADY) frame.rowconfigure(0, weight=1) for i in range(3): frame.columnconfigure(i, weight=1) label_frame = ttk.LabelFrame(frame, text="Groups", padding=FRAME_PAD) label_frame.grid(row=0, column=0, sticky="nsew") label_frame.rowconfigure(0, weight=1) label_frame.columnconfigure(0, weight=1) self.groups = ListboxScroll(label_frame) self.groups.grid(sticky="nsew") for group in sorted(self.app.core.config_services_groups): self.groups.listbox.insert(tk.END, group) self.groups.listbox.bind("<<ListboxSelect>>", self.handle_group_change) self.groups.listbox.selection_set(0) label_frame = ttk.LabelFrame(frame, text="Services") label_frame.grid(row=0, column=1, sticky="nsew") label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) self.services = CheckboxList( label_frame, self.app, clicked=self.service_clicked, padding=FRAME_PAD ) self.services.grid(sticky="nsew") label_frame = ttk.LabelFrame(frame, text="Selected", padding=FRAME_PAD) 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") frame = ttk.Frame(self.top) frame.grid(stick="ew") for i in range(4): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Configure", command=self.click_configure) button.grid(row=0, column=0, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Save", command=self.click_save) button.grid(row=0, column=1, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Remove", command=self.click_remove) button.grid(row=0, column=2, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Cancel", command=self.click_cancel) button.grid(row=0, column=3, sticky="ew") # trigger group change self.groups.listbox.event_generate("<<ListboxSelect>>") def handle_group_change(self, event: tk.Event = None): selection = self.groups.listbox.curselection() if selection: index = selection[0] group = self.groups.listbox.get(index) self.services.clear() for name in sorted(self.app.core.config_services_groups[group]): checked = name in self.current_services self.services.add(name, checked) def service_clicked(self, name: str, var: tk.IntVar): 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) if self.is_custom_service(name): self.current.listbox.itemconfig(tk.END, bg="green") 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, ) if not dialog.has_error: dialog.show() else: messagebox.showinfo( "Node service configuration", "Select a service to configure" ) def click_save(self): 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.destroy() def click_cancel(self): self.current_services = None self.destroy() def click_remove(self): cur = self.current.listbox.curselection() if cur: service = self.current.listbox.get(cur[0]) self.current.listbox.delete(cur[0]) self.current_services.remove(service) for checkbutton in self.services.frame.winfo_children(): if checkbutton["text"] == service: checkbutton.invoke() 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
class NodeServiceDialog(Dialog): def __init__(self, master, app, canvas_node, services=None): title = f"{canvas_node.core_node.name} Services" super().__init__(master, app, title, modal=True) self.app = app self.canvas_node = canvas_node self.node_id = canvas_node.core_node.id self.groups = None self.services = None self.current = None if services is None: services = canvas_node.core_node.services model = canvas_node.core_node.model if len(services) == 0: services = set(self.app.core.default_services[model]) else: services = set(services) self.current_services = services self.draw() def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) frame = ttk.Frame(self.top) frame.grid(stick="nsew", pady=PADY) frame.rowconfigure(0, weight=1) for i in range(3): frame.columnconfigure(i, weight=1) label_frame = ttk.LabelFrame(frame, text="Groups", padding=FRAME_PAD) label_frame.grid(row=0, column=0, sticky="nsew") label_frame.rowconfigure(0, weight=1) label_frame.columnconfigure(0, weight=1) self.groups = ListboxScroll(label_frame) self.groups.grid(sticky="nsew") for group in sorted(self.app.core.services): self.groups.listbox.insert(tk.END, group) self.groups.listbox.bind("<<ListboxSelect>>", self.handle_group_change) self.groups.listbox.selection_set(0) label_frame = ttk.LabelFrame(frame, text="Services") label_frame.grid(row=0, column=1, sticky="nsew") label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) self.services = CheckboxList( label_frame, self.app, clicked=self.service_clicked, padding=FRAME_PAD ) self.services.grid(sticky="nsew") label_frame = ttk.LabelFrame(frame, text="Selected", padding=FRAME_PAD) 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) frame = ttk.Frame(self.top) frame.grid(stick="ew") for i in range(3): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Configure", command=self.click_configure) button.grid(row=0, column=0, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Save", command=self.click_save) button.grid(row=0, column=1, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Cancel", command=self.click_cancel) button.grid(row=0, column=2, sticky="ew") # trigger group change self.groups.listbox.event_generate("<<ListboxSelect>>") def handle_group_change(self, event): selection = self.groups.listbox.curselection() if selection: index = selection[0] group = self.groups.listbox.get(index) self.services.clear() for name in sorted(self.app.core.services[group]): checked = name in self.current_services self.services.add(name, checked) 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) self.canvas_node.core_node.services[:] = self.current_services def click_configure(self): current_selection = self.current.listbox.curselection() if len(current_selection): dialog = ServiceConfigDialog( master=self, app=self.app, service_name=self.current.listbox.get(current_selection[0]), node_id=self.node_id, ) dialog.show() else: messagebox.showinfo( "Node service configuration", "Select a service to configure" ) def click_save(self): if ( self.current_services != self.app.core.default_services[self.canvas_node.core_node.model] ): self.canvas_node.core_node.services[:] = self.current_services else: if len(self.canvas_node.core_node.services) > 0: self.canvas_node.core_node.services[:] = [] self.destroy() def click_cancel(self): self.current_services = None self.destroy()