class ViewConfigDialog(Dialog): def __init__( self, app: "Application", master: tk.BaseWidget, name: str, service: str, file_name: str, data: str, ) -> None: title = f"{name} Service({service}) File({file_name})" super().__init__(app, title, master=master) self.data = data self.service_data = None self.draw() def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) self.service_data = CodeText(self.top) self.service_data.grid(sticky="nsew", pady=PADY) self.service_data.text.insert(tk.END, self.data) self.service_data.text.config(state=tk.DISABLED) button = ttk.Button(self.top, text="Close", command=self.destroy) button.grid(sticky="ew")
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)
class ErrorDialog(Dialog): def __init__(self, app: "Application", title: str, details: str) -> None: super().__init__(app, "CORE Exception") self.title = title self.details = details self.error_message = None self.draw() def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) frame = ttk.Frame(self.top, padding=FRAME_PAD) frame.grid(pady=PADY, sticky="ew") frame.columnconfigure(1, weight=1) image = Images.get(ImageEnum.ERROR, 36) label = ttk.Label(frame, image=image) label.image = image label.grid(row=0, column=0, padx=PADX) label = ttk.Label(frame, text=self.title) label.grid(row=0, column=1, sticky="ew") self.error_message = CodeText(self.top) self.error_message.text.insert("1.0", self.details) self.error_message.text.config(state="disabled") self.error_message.grid(sticky="nsew", pady=PADY) button = ttk.Button(self.top, text="Close", command=lambda: self.destroy()) button.grid(sticky="ew")
class ErrorDialog(Dialog): def __init__(self, app: "Application", title: str, details: str) -> None: super().__init__(app, "CORE Exception") self.title: str = title self.details: str = details self.error_message: Optional[CodeText] = None self.draw() def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) image = Images.get(ImageEnum.ERROR, 24) label = ttk.Label(self.top, text=self.title, image=image, compound=tk.LEFT, anchor=tk.CENTER) label.image = image label.grid(sticky=tk.EW, pady=PADY) self.error_message = CodeText(self.top) self.error_message.text.insert("1.0", self.details) self.error_message.text.config(state=tk.DISABLED) self.error_message.grid(sticky=tk.NSEW, pady=PADY) button = ttk.Button(self.top, text="Close", command=lambda: self.destroy()) button.grid(sticky=tk.EW)
class ViewConfigDialog(Dialog): def __init__( self, master: Any, app: "Application", node_id: int, data: str, filename: str = None, ): super().__init__(master, app, f"n{node_id} config data", modal=True) self.data = data self.service_data = None self.filepath = tk.StringVar(value=f"/tmp/services.tmp-n{node_id}-{filename}") self.draw() def draw(self): self.top.columnconfigure(0, weight=1) frame = ttk.Frame(self.top, padding=FRAME_PAD) frame.columnconfigure(0, weight=1) frame.columnconfigure(1, weight=10) frame.grid(row=0, column=0, sticky="ew") label = ttk.Label(frame, text="File: ") label.grid(row=0, column=0, sticky="ew", padx=PADX) entry = ttk.Entry(frame, textvariable=self.filepath) entry.config(state="disabled") entry.grid(row=0, column=1, sticky="ew") self.service_data = CodeText(self.top) self.service_data.grid(row=1, column=0, sticky="nsew") self.service_data.text.insert("end", self.data) self.service_data.text.config(state="disabled") button = ttk.Button(self.top, text="Close", command=self.destroy) button.grid(row=2, column=0, sticky="ew", padx=PADX)
def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) self.service_data = CodeText(self.top) self.service_data.grid(sticky="nsew", pady=PADY) self.service_data.text.insert(tk.END, self.data) self.service_data.text.config(state=tk.DISABLED) button = ttk.Button(self.top, text="Close", command=self.destroy) button.grid(sticky="ew")
def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) image = Images.get(ImageEnum.ERROR, 36) label = ttk.Label(self.top, image=image) label.image = image label.grid(row=0, column=0) self.error_message = CodeText(self.top) self.error_message.text.insert("1.0", self.details) self.error_message.text.config(state="disabled") self.error_message.grid(row=1, column=0, sticky="nsew")
def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) codetext = CodeText(self.top) codetext.text.insert("1.0", LICENSE) codetext.text.config(state=tk.DISABLED) codetext.grid(sticky="nsew") label = ttk.Label( self.top, text="Icons from https://icons8.com", anchor=tk.CENTER ) label.grid(sticky="ew")
def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) image = images.from_enum(ImageEnum.ERROR, width=images.ERROR_SIZE) label = ttk.Label( self.top, text=self.message, image=image, compound=tk.LEFT, anchor=tk.CENTER ) label.image = image label.grid(sticky=tk.W, pady=PADY) self.error_message = CodeText(self.top) self.error_message.text.insert("1.0", self.details) self.error_message.text.config(state=tk.DISABLED) self.error_message.grid(sticky=tk.EW, pady=PADY) button = ttk.Button(self.top, text="Close", command=lambda: self.destroy()) button.grid(sticky=tk.EW)
def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) # name and states frame = ttk.Frame(self.top) frame.grid(sticky="ew", pady=PADY) frame.columnconfigure(0, weight=2) frame.columnconfigure(1, weight=7) frame.columnconfigure(2, weight=1) label = ttk.Label(frame, text="Name") label.grid(row=0, column=0, sticky="ew", padx=PADX) entry = ttk.Entry(frame, textvariable=self.name) entry.grid(row=0, column=1, sticky="ew", padx=PADX) values = tuple(x for x in core_pb2.SessionState.Enum.keys() if x != "NONE") initial_state = core_pb2.SessionState.Enum.Name( core_pb2.SessionState.RUNTIME) self.state.set(initial_state) self.name.set(f"{initial_state.lower()}_hook.sh") combobox = ttk.Combobox(frame, textvariable=self.state, values=values, state="readonly") combobox.grid(row=0, column=2, sticky="ew") combobox.bind("<<ComboboxSelected>>", self.state_change) # data self.codetext = CodeText(self.top) self.codetext.text.insert( 1.0, ("#!/bin/sh\n" "# session hook script; write commands here to execute on the host at the\n" "# specified state\n"), ) self.codetext.grid(sticky="nsew", pady=PADY) # button row frame = ttk.Frame(self.top) frame.grid(sticky="ew") for i in range(2): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Save", command=lambda: self.save()) button.grid(row=0, column=0, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Cancel", command=lambda: self.destroy()) button.grid(row=0, column=1, sticky="ew")
class ErrorDialog(Dialog): def __init__(self, master, app: "Application", title: str, details: str): super().__init__(master, app, title, modal=True) self.error_message = None self.details = details self.draw() def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) image = Images.get(ImageEnum.ERROR, 36) label = ttk.Label(self.top, image=image) label.image = image label.grid(row=0, column=0) self.error_message = CodeText(self.top) self.error_message.text.insert("1.0", self.details) self.error_message.text.config(state="disabled") self.error_message.grid(row=1, column=0, sticky="nsew")
def draw(self): self.top.columnconfigure(0, weight=1) frame = ttk.Frame(self.top, padding=FRAME_PAD) frame.columnconfigure(0, weight=1) frame.columnconfigure(1, weight=10) frame.grid(row=0, column=0, sticky="ew") label = ttk.Label(frame, text="File: ") label.grid(row=0, column=0, sticky="ew", padx=PADX) entry = ttk.Entry(frame, textvariable=self.filepath) entry.config(state="disabled") entry.grid(row=0, column=1, sticky="ew") self.service_data = CodeText(self.top) self.service_data.grid(row=1, column=0, sticky="nsew") self.service_data.text.insert("end", self.data) self.service_data.text.config(state="disabled") button = ttk.Button(self.top, text="Close", command=self.destroy) button.grid(row=2, column=0, sticky="ew", padx=PADX)
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="Directories/Files") label = ttk.Label( tab, text="Directories and templates that will be used for this service." ) label.grid(pady=PADY) frame = ttk.Frame(tab) frame.grid(sticky=tk.EW, pady=PADY) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Directories") label.grid(row=0, column=0, sticky=tk.W, padx=PADX) directories_combobox = ttk.Combobox( frame, values=self.directories, state="readonly" ) directories_combobox.grid(row=0, column=1, sticky=tk.EW, pady=PADY) if self.directories: directories_combobox.current(0) label = ttk.Label(frame, text="Templates") label.grid(row=1, column=0, sticky=tk.W, padx=PADX) self.templates_combobox = ttk.Combobox( frame, values=self.templates, state="readonly" ) self.templates_combobox.bind( "<<ComboboxSelected>>", self.handle_template_changed ) self.templates_combobox.grid(row=1, column=1, sticky=tk.EW, pady=PADY) self.template_text = CodeText(tab) self.template_text.grid(sticky=tk.NSEW) tab.rowconfigure(self.template_text.grid_info()["row"], weight=1) if self.templates: self.templates_combobox.current(0) self.template_text.text.delete(1.0, "end") self.template_text.text.insert( "end", self.temp_service_files[self.templates[0]] ) self.template_text.text.bind("<FocusOut>", self.update_template_file_data)
def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) frame = ttk.Frame(self.top) frame.grid(row=0, column=0, sticky="ew", pady=PADY) frame.columnconfigure(0, weight=1) frame.columnconfigure(1, weight=9) label = ttk.Label(frame, text="File", anchor="w") label.grid(row=0, column=0, sticky="ew") entry = ttk.Entry(frame, textvariable=self.path, state="disabled") entry.grid(row=0, column=1, sticky="ew") try: file = open("/var/log/core-daemon.log", "r") log = file.readlines() except FileNotFoundError: log = "Log file not found" codetext = CodeText(self.top) codetext.text.insert("1.0", log) codetext.text.see("end") codetext.text.config(state=tk.DISABLED) codetext.grid(row=1, column=0, sticky="nsew")
def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) frame = ttk.Frame(self.top, padding=FRAME_PAD) frame.grid(pady=PADY, sticky="ew") frame.columnconfigure(1, weight=1) image = Images.get(ImageEnum.ERROR, 36) label = ttk.Label(frame, image=image) label.image = image label.grid(row=0, column=0, padx=PADX) label = ttk.Label(frame, text=self.title) label.grid(row=0, column=1, sticky="ew") self.error_message = CodeText(self.top) self.error_message.text.insert("1.0", self.details) self.error_message.text.config(state="disabled") self.error_message.grid(sticky="nsew", pady=PADY) button = ttk.Button(self.top, text="Close", command=lambda: self.destroy()) button.grid(sticky="ew")
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_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)
class HookDialog(Dialog): def __init__(self, master: tk.BaseWidget, app: "Application") -> None: super().__init__(app, "Hook", master=master) self.name: tk.StringVar = tk.StringVar() self.codetext: Optional[CodeText] = None self.hook: Optional[Hook] = None self.state: tk.StringVar = tk.StringVar() self.editing: bool = False self.draw() def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) # name and states frame = ttk.Frame(self.top) frame.grid(sticky=tk.EW, pady=PADY) frame.columnconfigure(0, weight=2) frame.columnconfigure(1, weight=7) frame.columnconfigure(2, weight=1) label = ttk.Label(frame, text="Name") label.grid(row=0, column=0, sticky=tk.EW, padx=PADX) entry = ttk.Entry(frame, textvariable=self.name) entry.grid(row=0, column=1, sticky=tk.EW, padx=PADX) values = tuple(x.name for x in SessionState) initial_state = SessionState.RUNTIME.name self.state.set(initial_state) self.name.set(f"{initial_state.lower()}_hook.sh") combobox = ttk.Combobox(frame, textvariable=self.state, values=values, state="readonly") combobox.grid(row=0, column=2, sticky=tk.EW) combobox.bind("<<ComboboxSelected>>", self.state_change) # data self.codetext = CodeText(self.top) self.codetext.text.insert( 1.0, ("#!/bin/sh\n" "# session hook script; write commands here to execute on the host at the\n" "# specified state\n"), ) self.codetext.grid(sticky=tk.NSEW, pady=PADY) # button row 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=lambda: self.save()) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Cancel", command=lambda: self.destroy()) button.grid(row=0, column=1, sticky=tk.EW) def state_change(self, event: tk.Event) -> None: if self.editing: return state_name = self.state.get() self.name.set(f"{state_name.lower()}_hook.sh") def set(self, hook: Hook) -> None: self.editing = True self.hook = hook self.name.set(hook.file) self.codetext.text.delete(1.0, tk.END) self.codetext.text.insert(tk.END, hook.data) state_name = hook.state.name self.state.set(state_name) def save(self) -> None: data = self.codetext.text.get("1.0", tk.END).strip() state = SessionState[self.state.get()] file_name = self.name.get() if self.editing: self.hook.state = state self.hook.file = file_name self.hook.data = data else: self.hook = Hook(state=state, file=file_name, data=data) self.destroy()
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 AlertsDialog(Dialog): def __init__(self, master: "Application", app: "Application"): super().__init__(master, app, "Alerts", modal=True) self.app = app self.tree = None self.codetext = None self.alarm_map = {} self.draw() def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) frame = ttk.Frame(self.top) frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) frame.grid(sticky="nsew", pady=PADY) self.tree = ttk.Treeview( frame, columns=("time", "level", "session_id", "node", "source"), show="headings", ) self.tree.grid(row=0, column=0, sticky="nsew") self.tree.column("time", stretch=tk.YES) self.tree.heading("time", text="Time") self.tree.column("level", stretch=tk.YES, width=100) self.tree.heading("level", text="Level") self.tree.column("session_id", stretch=tk.YES, width=100) self.tree.heading("session_id", text="Session ID") self.tree.column("node", stretch=tk.YES, width=100) self.tree.heading("node", text="Node") self.tree.column("source", stretch=tk.YES, width=100) self.tree.heading("source", text="Source") self.tree.bind("<<TreeviewSelect>>", self.click_select) for alarm in self.app.statusbar.core_alarms: exception = alarm.exception_event level_name = ExceptionLevel.Enum.Name(exception.level) insert_id = self.tree.insert( "", tk.END, text=exception.date, values=( exception.date, level_name, alarm.session_id, exception.node_id, exception.source, ), tags=(level_name, ), ) self.alarm_map[insert_id] = alarm error_name = ExceptionLevel.Enum.Name(ExceptionLevel.ERROR) self.tree.tag_configure(error_name, background="#ff6666") fatal_name = ExceptionLevel.Enum.Name(ExceptionLevel.FATAL) self.tree.tag_configure(fatal_name, background="#d9d9d9") warning_name = ExceptionLevel.Enum.Name(ExceptionLevel.WARNING) self.tree.tag_configure(warning_name, background="#ffff99") notice_name = ExceptionLevel.Enum.Name(ExceptionLevel.NOTICE) self.tree.tag_configure(notice_name, background="#85e085") yscrollbar = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview) yscrollbar.grid(row=0, column=1, sticky="ns") self.tree.configure(yscrollcommand=yscrollbar.set) xscrollbar = ttk.Scrollbar(frame, orient="horizontal", command=self.tree.xview) xscrollbar.grid(row=1, sticky="ew") self.tree.configure(xscrollcommand=xscrollbar.set) self.codetext = CodeText(self.top) self.codetext.text.config(state=tk.DISABLED, height=11) self.codetext.grid(sticky="nsew", pady=PADY) frame = ttk.Frame(self.top) frame.grid(sticky="ew") frame.columnconfigure(0, weight=1) frame.columnconfigure(1, weight=1) frame.columnconfigure(2, weight=1) frame.columnconfigure(3, weight=1) button = ttk.Button(frame, text="Reset", command=self.reset_alerts) button.grid(row=0, column=0, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Daemon Log", command=self.daemon_log) button.grid(row=0, column=1, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Node Log") button.grid(row=0, column=2, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Close", command=self.destroy) button.grid(row=0, column=3, sticky="ew") def reset_alerts(self): self.codetext.text.delete("1.0", tk.END) for item in self.tree.get_children(): self.tree.delete(item) self.app.statusbar.core_alarms.clear() def daemon_log(self): dialog = DaemonLog(self, self.app) dialog.show() def click_select(self, event: tk.Event): current = self.tree.selection()[0] alarm = self.alarm_map[current] self.codetext.text.config(state=tk.NORMAL) self.codetext.text.delete("1.0", "end") self.codetext.text.insert("1.0", alarm.exception_event.text) self.codetext.text.config(state=tk.DISABLED)
class ServiceConfigDialog(Dialog): def __init__(self, master: Any, app: "Application", service_name: str, node_id: int): title = f"{service_name} Service" super().__init__(master, app, title, modal=True) self.master = master self.app = app self.core = app.core self.node_id = node_id self.service_name = service_name self.service_configs = app.core.service_configs self.file_configs = app.core.file_configs self.radiovar = tk.IntVar() self.radiovar.set(2) self.metadata = "" self.filenames = [] self.dependencies = [] self.executables = [] self.startup_commands = [] self.validation_commands = [] self.shutdown_commands = [] self.default_startup = [] self.default_validate = [] self.default_shutdown = [] self.validation_mode = None self.validation_time = None self.validation_period = None self.documentnew_img = Images.get(ImageEnum.DOCUMENTNEW, 16) self.editdelete_img = Images.get(ImageEnum.EDITDELETE, 16) self.notebook = None self.metadata_entry = None self.filename_combobox = None self.startup_commands_listbox = None self.shutdown_commands_listbox = None self.validate_commands_listbox = None self.validation_time_entry = None self.validation_mode_entry = None self.service_file_data = None self.validation_period_entry = None self.original_service_files = {} self.temp_service_files = {} self.modified_files = set() self.has_error = False self.load() if not self.has_error: self.draw() def load(self) -> bool: 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[:] custom_configs = self.service_configs if (self.node_id in custom_configs and self.service_name in custom_configs[self.node_id]): service_config = custom_configs[self.node_id][ self.service_name] else: service_config = 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.original_service_files = { x: self.app.core.get_node_service_file(self.node_id, self.service_name, x) for x in self.filenames } self.temp_service_files = dict(self.original_service_files) file_configs = self.file_configs if (self.node_id in file_configs and self.service_name in file_configs[self.node_id]): for file, data in file_configs[self.node_id][ self.service_name].items(): self.temp_service_files[file] = data except grpc.RpcError as e: self.has_error = True show_grpc_error(e, self.master, self.app) def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) # draw metadata frame = ttk.Frame(self.top) frame.grid(sticky="ew", pady=PADY) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Meta-data") label.grid(row=0, column=0, sticky="w", padx=PADX) self.metadata_entry = ttk.Entry(frame, textvariable=self.metadata) self.metadata_entry.grid(row=0, column=1, sticky="ew") # draw notebook self.notebook = ttk.Notebook(self.top) self.notebook.grid(sticky="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): tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky="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="ew", pady=PADY) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="File Name") label.grid(row=0, column=0, padx=PADX, sticky="w") self.filename_combobox = ttk.Combobox(frame, values=self.filenames, state="readonly") self.filename_combobox.bind("<<ComboboxSelected>>", self.display_service_file_data) self.filename_combobox.grid(row=0, column=1, sticky="ew", padx=PADX) button = ttk.Button(frame, image=self.documentnew_img, state="disabled") button.bind("<Button-1>", self.add_filename) button.grid(row=0, column=2, padx=PADX) button = ttk.Button(frame, image=self.editdelete_img, state="disabled") button.bind("<Button-1>", self.delete_filename) button.grid(row=0, column=3) frame = ttk.Frame(tab) frame.grid(sticky="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="w", padx=PADX) entry = ttk.Entry(frame, state=tk.DISABLED) entry.grid(row=0, column=1, sticky="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="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="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="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): tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky="nsew") tab.columnconfigure(0, 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() def draw_tab_startstop(self): tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky="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="nsew", pady=PADY) frame = ttk.Frame(label_frame) frame.grid(row=0, column=0, sticky="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="ew", padx=PADX) button = ttk.Button(frame, image=self.editdelete_img) button.grid(row=0, column=2, sticky="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="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): tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab.grid(sticky="nsew") tab.columnconfigure(0, weight=1) self.notebook.add(tab, text="Configuration", sticky="nsew") frame = ttk.Frame(tab) frame.grid(sticky="ew", pady=PADY) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Validation Time") label.grid(row=0, column=0, sticky="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="ew", pady=PADY) label = ttk.Label(frame, text="Validation Mode") label.grid(row=1, column=0, sticky="w", padx=PADX) if self.validation_mode == core_pb2.ServiceValidationMode.BLOCKING: mode = "BLOCKING" elif self.validation_mode == core_pb2.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="ew", pady=PADY) label = ttk.Label(frame, text="Validation Period") label.grid(row=2, column=0, sticky="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="ew", pady=PADY) label_frame = ttk.LabelFrame(tab, text="Executables", padding=FRAME_PAD) label_frame.grid(sticky="nsew", pady=PADY) label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) listbox_scroll = ListboxScroll(label_frame) listbox_scroll.grid(sticky="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="nsew", pady=PADY) label_frame.columnconfigure(0, weight=1) label_frame.rowconfigure(0, weight=1) listbox_scroll = ListboxScroll(label_frame) listbox_scroll.grid(sticky="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): frame = ttk.Frame(self.top) frame.grid(sticky="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="ew", padx=PADX) button = ttk.Button(frame, text="Defaults", command=self.click_defaults) button.grid(row=0, column=1, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Copy...", command=self.click_copy) button.grid(row=0, column=2, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Cancel", command=self.destroy) button.grid(row=0, column=3, sticky="ew") def add_filename(self, event: tk.Event): # not worry about it for now return frame_contains_button = event.widget.master combobox = frame_contains_button.grid_slaves(row=0, column=1)[0] filename = combobox.get() if filename not in combobox["values"]: combobox["values"] += (filename, ) def delete_filename(self, event: tk.Event): # not worry about it for now return frame_comntains_button = event.widget.master combobox = frame_comntains_button.grid_slaves(row=0, column=1)[0] filename = combobox.get() if filename in combobox["values"]: combobox["values"] = tuple( [x for x in combobox["values"] if x != filename]) combobox.set("") def add_command(self, event: tk.Event): 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) def update_entry(self, event: tk.Event): 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) def delete_command(self, event: tk.Event): 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): current_listbox = self.master.current.listbox if not self.is_custom_service_config( ) and not self.is_custom_service_file(): if self.node_id in self.service_configs: self.service_configs[self.node_id].pop(self.service_name, None) current_listbox.itemconfig(current_listbox.curselection()[0], bg="") self.destroy() return try: if self.is_custom_service_config(): startup_commands = self.startup_commands_listbox.get(0, "end") shutdown_commands = self.shutdown_commands_listbox.get( 0, "end") validate_commands = self.validate_commands_listbox.get( 0, "end") config = self.core.set_node_service( self.node_id, self.service_name, startups=startup_commands, validations=validate_commands, shutdowns=shutdown_commands, ) if self.node_id not in self.service_configs: self.service_configs[self.node_id] = {} self.service_configs[self.node_id][self.service_name] = config for file in self.modified_files: if self.node_id not in self.file_configs: self.file_configs[self.node_id] = {} if self.service_name not in self.file_configs[self.node_id]: self.file_configs[self.node_id][self.service_name] = {} self.file_configs[self.node_id][ self.service_name][file] = self.temp_service_files[file] self.app.core.set_node_service_file( self.node_id, self.service_name, file, self.temp_service_files[file]) all_current = current_listbox.get(0, tk.END) current_listbox.itemconfig(all_current.index(self.service_name), bg="green") except grpc.RpcError as e: show_grpc_error(e, self.top, self.app) self.destroy() def display_service_file_data(self, event: tk.Event): combobox = event.widget 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): scrolledtext = event.widget filename = self.filename_combobox.get() self.temp_service_files[filename] = scrolledtext.get(1.0, "end") if self.temp_service_files[filename] != self.original_service_files[ filename]: self.modified_files.add(filename) else: self.modified_files.discard(filename) def is_custom_service_config(self): startup_commands = self.startup_commands_listbox.get(0, "end") shutdown_commands = self.shutdown_commands_listbox.get(0, "end") validate_commands = self.validate_commands_listbox.get(0, "end") return (set(self.default_startup) != set(startup_commands) or set(self.default_validate) != set(validate_commands) or set(self.default_shutdown) != set(shutdown_commands)) def is_custom_service_file(self): return len(self.modified_files) > 0 def click_defaults(self): if self.node_id in self.service_configs: self.service_configs[self.node_id].pop(self.service_name, None) if self.node_id in self.file_configs: self.file_configs[self.node_id].pop(self.service_name, None) self.temp_service_files = dict(self.original_service_files) 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]) 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) def click_copy(self): dialog = CopyServiceConfigDialog(self, self.app, self.node_id) dialog.show() def append_commands(self, commands: List[str], listbox: tk.Listbox, to_add: List[str]): for cmd in to_add: commands.append(cmd) listbox.insert(tk.END, cmd)
def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) frame = ttk.Frame(self.top) frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) frame.grid(sticky=tk.NSEW, pady=PADY) self.tree = ttk.Treeview( frame, columns=("time", "level", "session_id", "node", "source"), show="headings", ) self.tree.grid(row=0, column=0, sticky=tk.NSEW) self.tree.column("time", stretch=tk.YES) self.tree.heading("time", text="Time") self.tree.column("level", stretch=tk.YES, width=100) self.tree.heading("level", text="Level") self.tree.column("session_id", stretch=tk.YES, width=100) self.tree.heading("session_id", text="Session ID") self.tree.column("node", stretch=tk.YES, width=100) self.tree.heading("node", text="Node") self.tree.column("source", stretch=tk.YES, width=100) self.tree.heading("source", text="Source") self.tree.bind("<<TreeviewSelect>>", self.click_select) for exception in self.app.statusbar.core_alarms: level_name = exception.level.name node_id = exception.node_id if exception.node_id else "" insert_id = self.tree.insert( "", tk.END, text=exception.date, values=( exception.date, level_name, exception.session_id, node_id, exception.source, ), tags=(level_name, ), ) self.alarm_map[insert_id] = exception error_name = ExceptionLevel.ERROR.name self.tree.tag_configure(error_name, background="#ff6666") fatal_name = ExceptionLevel.FATAL.name self.tree.tag_configure(fatal_name, background="#d9d9d9") warning_name = ExceptionLevel.WARNING.name self.tree.tag_configure(warning_name, background="#ffff99") notice_name = ExceptionLevel.NOTICE.name self.tree.tag_configure(notice_name, background="#85e085") yscrollbar = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview) yscrollbar.grid(row=0, column=1, sticky=tk.NS) self.tree.configure(yscrollcommand=yscrollbar.set) xscrollbar = ttk.Scrollbar(frame, orient="horizontal", command=self.tree.xview) xscrollbar.grid(row=1, sticky=tk.EW) self.tree.configure(xscrollcommand=xscrollbar.set) self.codetext = CodeText(self.top) self.codetext.text.config(state=tk.DISABLED, height=11) self.codetext.grid(sticky=tk.NSEW, pady=PADY) frame = ttk.Frame(self.top) frame.grid(sticky=tk.EW) frame.columnconfigure(0, weight=1) frame.columnconfigure(1, weight=1) button = ttk.Button(frame, text="Reset", command=self.reset_alerts) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Close", command=self.destroy) button.grid(row=0, column=1, sticky=tk.EW)
class AlertsDialog(Dialog): def __init__(self, app: "Application") -> None: super().__init__(app, "Alerts") self.tree: Optional[ttk.Treeview] = None self.codetext: Optional[CodeText] = None self.alarm_map: Dict[int, ExceptionEvent] = {} self.draw() def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) frame = ttk.Frame(self.top) frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) frame.grid(sticky=tk.NSEW, pady=PADY) self.tree = ttk.Treeview( frame, columns=("time", "level", "session_id", "node", "source"), show="headings", ) self.tree.grid(row=0, column=0, sticky=tk.NSEW) self.tree.column("time", stretch=tk.YES) self.tree.heading("time", text="Time") self.tree.column("level", stretch=tk.YES, width=100) self.tree.heading("level", text="Level") self.tree.column("session_id", stretch=tk.YES, width=100) self.tree.heading("session_id", text="Session ID") self.tree.column("node", stretch=tk.YES, width=100) self.tree.heading("node", text="Node") self.tree.column("source", stretch=tk.YES, width=100) self.tree.heading("source", text="Source") self.tree.bind("<<TreeviewSelect>>", self.click_select) for exception in self.app.statusbar.core_alarms: level_name = exception.level.name node_id = exception.node_id if exception.node_id else "" insert_id = self.tree.insert( "", tk.END, text=exception.date, values=( exception.date, level_name, exception.session_id, node_id, exception.source, ), tags=(level_name, ), ) self.alarm_map[insert_id] = exception error_name = ExceptionLevel.ERROR.name self.tree.tag_configure(error_name, background="#ff6666") fatal_name = ExceptionLevel.FATAL.name self.tree.tag_configure(fatal_name, background="#d9d9d9") warning_name = ExceptionLevel.WARNING.name self.tree.tag_configure(warning_name, background="#ffff99") notice_name = ExceptionLevel.NOTICE.name self.tree.tag_configure(notice_name, background="#85e085") yscrollbar = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview) yscrollbar.grid(row=0, column=1, sticky=tk.NS) self.tree.configure(yscrollcommand=yscrollbar.set) xscrollbar = ttk.Scrollbar(frame, orient="horizontal", command=self.tree.xview) xscrollbar.grid(row=1, sticky=tk.EW) self.tree.configure(xscrollcommand=xscrollbar.set) self.codetext = CodeText(self.top) self.codetext.text.config(state=tk.DISABLED, height=11) self.codetext.grid(sticky=tk.NSEW, pady=PADY) frame = ttk.Frame(self.top) frame.grid(sticky=tk.EW) frame.columnconfigure(0, weight=1) frame.columnconfigure(1, weight=1) button = ttk.Button(frame, text="Reset", command=self.reset_alerts) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) button = ttk.Button(frame, text="Close", command=self.destroy) button.grid(row=0, column=1, sticky=tk.EW) def reset_alerts(self) -> None: self.codetext.text.config(state=tk.NORMAL) self.codetext.text.delete(1.0, tk.END) self.codetext.text.config(state=tk.DISABLED) for item in self.tree.get_children(): self.tree.delete(item) self.app.statusbar.clear_alerts() def click_select(self, event: tk.Event) -> None: current = self.tree.selection()[0] exception = self.alarm_map[current] self.codetext.text.config(state=tk.NORMAL) self.codetext.text.delete(1.0, tk.END) self.codetext.text.insert(1.0, exception.text) self.codetext.text.config(state=tk.DISABLED)
class ConfigServiceConfigDialog(Dialog): def __init__( self, master: tk.BaseWidget, app: "Application", service_name: str, node: Node ) -> None: title = f"{service_name} Config Service" super().__init__(app, title, master=master) self.core: "CoreClient" = app.core self.node: Node = node self.service_name: str = service_name self.radiovar: tk.IntVar = tk.IntVar() self.radiovar.set(2) self.directories: List[str] = [] self.templates: 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: tk.StringVar = tk.StringVar() self.modes: List[str] = [] self.mode_configs: Dict[str, Dict[str, str]] = {} self.notebook: Optional[ttk.Notebook] = None self.templates_combobox: Optional[ttk.Combobox] = None self.modes_combobox: Optional[ttk.Combobox] = 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.template_text: Optional[CodeText] = None self.validation_period_entry: Optional[ttk.Entry] = None self.original_service_files: Dict[str, str] = {} self.temp_service_files: Dict[str, str] = {} self.modified_files: Set[str] = set() self.config_frame: Optional[ConfigFrame] = None self.default_config: Dict[str, str] = {} self.config: Dict[str, ConfigOption] = {} self.has_error: bool = False self.load() if not self.has_error: self.draw() def load(self) -> None: try: self.core.start_session(definition=True) service = self.core.config_services[self.service_name] self.dependencies = service.dependencies[:] self.executables = service.executables[:] self.directories = service.directories[:] self.templates = service.files[:] self.startup_commands = service.startup[:] self.validation_commands = service.validate[:] self.shutdown_commands = service.shutdown[:] self.validation_mode = service.validation_mode self.validation_time = service.validation_timer self.validation_period.set(service.validation_period) defaults = self.core.client.get_config_service_defaults(self.service_name) self.original_service_files = defaults.templates self.temp_service_files = dict(self.original_service_files) self.modes = sorted(defaults.modes) self.mode_configs = defaults.modes self.config = ConfigOption.from_dict(defaults.config) self.default_config = {x.name: x.value for x in self.config.values()} service_config = self.node.config_service_configs.get(self.service_name) if service_config: for key, value in service_config.config.items(): self.config[key].value = value logger.info("default config: %s", self.default_config) for file, data in service_config.templates.items(): self.modified_files.add(file) self.temp_service_files[file] = data except grpc.RpcError as e: self.app.show_grpc_exception("Get Config Service Error", e) self.has_error = True def draw(self) -> None: self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) # draw notebook self.notebook = ttk.Notebook(self.top) self.notebook.grid(sticky=tk.NSEW, pady=PADY) self.draw_tab_files() if self.config: self.draw_tab_config() self.draw_tab_startstop() self.draw_tab_validation() 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="Directories/Files") label = ttk.Label( tab, text="Directories and templates that will be used for this service." ) label.grid(pady=PADY) frame = ttk.Frame(tab) frame.grid(sticky=tk.EW, pady=PADY) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Directories") label.grid(row=0, column=0, sticky=tk.W, padx=PADX) directories_combobox = ttk.Combobox( frame, values=self.directories, state="readonly" ) directories_combobox.grid(row=0, column=1, sticky=tk.EW, pady=PADY) if self.directories: directories_combobox.current(0) label = ttk.Label(frame, text="Templates") label.grid(row=1, column=0, sticky=tk.W, padx=PADX) self.templates_combobox = ttk.Combobox( frame, values=self.templates, state="readonly" ) self.templates_combobox.bind( "<<ComboboxSelected>>", self.handle_template_changed ) self.templates_combobox.grid(row=1, column=1, sticky=tk.EW, pady=PADY) self.template_text = CodeText(tab) self.template_text.grid(sticky=tk.NSEW) tab.rowconfigure(self.template_text.grid_info()["row"], weight=1) if self.templates: self.templates_combobox.current(0) self.template_text.text.delete(1.0, "end") self.template_text.text.insert( "end", self.temp_service_files[self.templates[0]] ) self.template_text.text.bind("<FocusOut>", self.update_template_file_data) def draw_tab_config(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") if self.modes: frame = ttk.Frame(tab) frame.grid(sticky=tk.EW, pady=PADY) frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Modes") label.grid(row=0, column=0, padx=PADX) self.modes_combobox = ttk.Combobox( frame, values=self.modes, state="readonly" ) self.modes_combobox.bind("<<ComboboxSelected>>", self.handle_mode_changed) self.modes_combobox.grid(row=0, column=1, sticky=tk.EW, pady=PADY) logger.info("config service config: %s", self.config) self.config_frame = ConfigFrame(tab, self.app, self.config) self.config_frame.draw_config() self.config_frame.grid(sticky=tk.NSEW, pady=PADY) tab.rowconfigure(self.config_frame.grid_info()["row"], weight=1) 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 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_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 click_apply(self) -> None: current_listbox = self.master.current.listbox if not self.is_custom(): self.node.config_service_configs.pop(self.service_name, None) current_listbox.itemconfig(current_listbox.curselection()[0], bg="") self.destroy() return service_config = self.node.config_service_configs.setdefault( self.service_name, ConfigServiceData() ) if self.config_frame: self.config_frame.parse_config() service_config.config = {x.name: x.value for x in self.config.values()} for file in self.modified_files: service_config.templates[file] = self.temp_service_files[file] all_current = current_listbox.get(0, tk.END) current_listbox.itemconfig(all_current.index(self.service_name), bg="green") self.destroy() def handle_template_changed(self, event: tk.Event) -> None: template = self.templates_combobox.get() self.template_text.text.delete(1.0, "end") self.template_text.text.insert("end", self.temp_service_files[template]) def handle_mode_changed(self, event: tk.Event) -> None: mode = self.modes_combobox.get() config = self.mode_configs[mode] logger.info("mode config: %s", config) self.config_frame.set_values(config) def update_template_file_data(self, event: tk.Event) -> None: scrolledtext = event.widget template = self.templates_combobox.get() self.temp_service_files[template] = scrolledtext.get(1.0, "end") if self.temp_service_files[template] != self.original_service_files[template]: self.modified_files.add(template) else: self.modified_files.discard(template) def is_custom(self) -> bool: has_custom_templates = len(self.modified_files) > 0 has_custom_config = False if self.config_frame: current = self.config_frame.parse_config() has_custom_config = self.default_config != current return has_custom_templates or has_custom_config def click_defaults(self) -> None: self.node.config_service_configs.pop(self.service_name, None) logger.info( "cleared config service config: %s", self.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") self.template_text.text.insert("end", self.temp_service_files[filename]) if self.config_frame: logger.info("resetting defaults: %s", self.default_config) self.config_frame.set_values(self.default_config) def click_copy(self) -> None: pass def append_commands( self, commands: List[str], listbox: tk.Listbox, to_add: List[str] ) -> None: for cmd in to_add: commands.append(cmd) listbox.insert(tk.END, cmd)
class HookDialog(Dialog): def __init__(self, master, app): super().__init__(master, app, "Hook", modal=True) self.name = tk.StringVar() self.codetext = None self.hook = core_pb2.Hook() self.state = tk.StringVar() self.draw() def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(1, weight=1) # name and states frame = ttk.Frame(self.top) frame.grid(sticky="ew", pady=PADY) frame.columnconfigure(0, weight=2) frame.columnconfigure(1, weight=7) frame.columnconfigure(2, weight=1) label = ttk.Label(frame, text="Name") label.grid(row=0, column=0, sticky="ew", padx=PADX) entry = ttk.Entry(frame, textvariable=self.name) entry.grid(row=0, column=1, sticky="ew", padx=PADX) values = tuple(x for x in core_pb2.SessionState.Enum.keys() if x != "NONE") initial_state = core_pb2.SessionState.Enum.Name( core_pb2.SessionState.RUNTIME) self.state.set(initial_state) self.name.set(f"{initial_state.lower()}_hook.sh") combobox = ttk.Combobox(frame, textvariable=self.state, values=values, state="readonly") combobox.grid(row=0, column=2, sticky="ew") combobox.bind("<<ComboboxSelected>>", self.state_change) # data self.codetext = CodeText(self.top) self.codetext.text.insert( 1.0, ("#!/bin/sh\n" "# session hook script; write commands here to execute on the host at the\n" "# specified state\n"), ) self.codetext.grid(sticky="nsew", pady=PADY) # button row frame = ttk.Frame(self.top) frame.grid(sticky="ew") for i in range(2): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Save", command=lambda: self.save()) button.grid(row=0, column=0, sticky="ew", padx=PADX) button = ttk.Button(frame, text="Cancel", command=lambda: self.destroy()) button.grid(row=0, column=1, sticky="ew") def state_change(self, event): state_name = self.state.get() self.name.set(f"{state_name.lower()}_hook.sh") def set(self, hook): self.hook = hook self.name.set(hook.file) self.codetext.text.delete(1.0, tk.END) self.codetext.text.insert(tk.END, hook.data) state_name = core_pb2.SessionState.Enum.Name(hook.state) self.state.set(state_name) def save(self): data = self.codetext.text.get("1.0", tk.END).strip() state_value = core_pb2.SessionState.Enum.Value(self.state.get()) self.hook.file = self.name.get() self.hook.data = data self.hook.state = state_value self.destroy()