Example #1
0
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")
Example #2
0
    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)
Example #3
0
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")
Example #4
0
File: error.py Project: umr-ds/core
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)
Example #5
0
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)
Example #6
0
 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")
Example #7
0
 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")
Example #8
0
    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")
Example #9
0
 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)
Example #10
0
    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")
Example #11
0
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")
Example #12
0
    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)
Example #13
0
    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)
Example #14
0
 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")
Example #15
0
    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")
Example #16
0
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)
Example #17
0
    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)
Example #18
0
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()
Example #19
0
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)
Example #20
0
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)
Example #21
0
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)
Example #22
0
    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)
Example #23
0
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)
Example #24
0
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)
Example #25
0
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()