commit 21b8cc283d83d4e932899ced15ff3ade781b156c Author: braniz Date: Thu Nov 16 09:50:25 2023 +0100 Initial diff --git a/README.md b/README.md new file mode 100644 index 0000000..883a0f7 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# LXC Manage GUI diff --git a/__pycache__/create.cpython-311.pyc b/__pycache__/create.cpython-311.pyc new file mode 100644 index 0000000..b108b12 Binary files /dev/null and b/__pycache__/create.cpython-311.pyc differ diff --git a/__pycache__/json.cpython-311.pyc b/__pycache__/json.cpython-311.pyc new file mode 100644 index 0000000..1f531d5 Binary files /dev/null and b/__pycache__/json.cpython-311.pyc differ diff --git a/__pycache__/lxc_ls.cpython-311.pyc b/__pycache__/lxc_ls.cpython-311.pyc new file mode 100644 index 0000000..7d25272 Binary files /dev/null and b/__pycache__/lxc_ls.cpython-311.pyc differ diff --git a/__pycache__/lxc_ls1.cpython-311.pyc b/__pycache__/lxc_ls1.cpython-311.pyc new file mode 100644 index 0000000..ada2ab9 Binary files /dev/null and b/__pycache__/lxc_ls1.cpython-311.pyc differ diff --git a/create.py b/create.py new file mode 100644 index 0000000..8fda4c2 --- /dev/null +++ b/create.py @@ -0,0 +1,128 @@ +import tkinter as tk +from tkinter import ttk, messagebox +from subprocess import Popen, PIPE + +class LxcLauncherGUI: + def __init__(self, root): + self.root = root + self.root.title("LXC Launcher") + + # Neue Variable für den Hostnamen + self.host_name_var = tk.StringVar() + + # Neue Variable für die Anzahl der zu erstellenden Hosts + self.num_hosts_var = tk.StringVar() + self.num_hosts_var.set("1") # Standardwert auf 1 setzen + + self.create_widgets() + + def create_widgets(self): + # Label + label = ttk.Label(self.root, text="Wähle ein LXC-Image:") + label.grid(row=0, column=0, padx=10, pady=10, sticky="w") + + # Auswahlmenü + images = self.get_lxc_images() + self.selected_image = tk.StringVar() + image_menu = ttk.Combobox(self.root, textvariable=self.selected_image, values=images) + image_menu.grid(row=0, column=1, padx=10, pady=10, sticky="w") + + # Eingabefeld für den Hostnamen + host_label = ttk.Label(self.root, text="Host-Name:") + host_label.grid(row=1, column=0, padx=10, pady=10, sticky="w") + + host_entry = ttk.Entry(self.root, textvariable=self.host_name_var) + host_entry.grid(row=1, column=1, padx=10, pady=10, sticky="w") + + # Eingabefeld für die Anzahl der zu erstellenden Hosts + num_hosts_label = ttk.Label(self.root, text="Anzahl der Hosts:") + num_hosts_label.grid(row=2, column=0, padx=10, pady=10, sticky="w") + + num_hosts_entry = ttk.Entry(self.root, textvariable=self.num_hosts_var) + num_hosts_entry.grid(row=2, column=1, padx=10, pady=10, sticky="w") + + # Checkbox für Nesting + self.nesting_checkbox_var = tk.BooleanVar() + nesting_checkbox = ttk.Checkbutton(self.root, text="Nesting", variable=self.nesting_checkbox_var) + nesting_checkbox.grid(row=3, column=0, padx=10, pady=10, sticky="w") + + # Checkbox für Protection + self.protection_checkbox_var = tk.BooleanVar() + protection_checkbox = ttk.Checkbutton(self.root, text="Protection", variable=self.protection_checkbox_var) + protection_checkbox.grid(row=3, column=1, padx=10, pady=10, sticky="w") + + # Button zum Starten + launch_button = ttk.Button(self.root, text="Starten", command=self.launch_lxc) + launch_button.grid(row=4, column=0, padx=10, pady=10, sticky="w") + + # Button für Weitere Hosts + hosts_button = ttk.Button(self.root, text="Weitere Hosts", command=self.show_hosts) + hosts_button.grid(row=4, column=1, padx=10, pady=10, sticky="w") + + # Button zum Beenden + exit_button = ttk.Button(self.root, text="Beenden", command=self.root.destroy) + exit_button.grid(row=5, column=0, columnspan=4, padx=10, pady=10, sticky="w") + + # Label für das Ergebnis + self.result_label = ttk.Label(self.root, text="") + self.result_label.grid(row=6, column=0, columnspan=4, padx=10, pady=10, sticky="w") + + def show_error_message(self, message): + messagebox.showerror("Fehler", message) + + def show_info_message(self, message): + messagebox.showinfo("OK", message) + + def get_lxc_images(self): + command = "lxc image list images: | awk -F'|' '{ print $2}' | sed '/^[[:space:]]*$/d' | awk -F'/' '{ print $1\"/\"$2 }' | sort | uniq | grep -vE 'more|ALIAS' | grep -iE 'Debian|CentOS|Ubuntu'" + process = Popen(command, shell=True, stdout=PIPE, stderr=PIPE) + output, error = process.communicate() + images = [line.strip() for line in output.decode("utf-8").split('\n') if line.strip()] + return images + + def launch_lxc(self): + selected_image = self.selected_image.get() + host_name_prefix = self.host_name_var.get() + + if selected_image and host_name_prefix: + num_hosts = self.num_hosts_var.get() + if not num_hosts.isdigit() or int(num_hosts) <= 0: + self.show_error_message("Bitte geben Sie eine gültige positive Ganzzahl für die Anzahl der Hosts ein.") + return + + created_hosts = [] # Eine Liste, um die erstellten Hostnamen zu speichern + + for i in range(1, int(num_hosts) + 1): + # Erstellen des Hostnamens mit Ordnungszahl + host_name = f"{host_name_prefix}{i}" + + nesting_option = "true" if self.nesting_checkbox_var.get() else "false" + protection_option = "true" if self.protection_checkbox_var.get() else "false" + launch_command = f"lxc launch images:{selected_image} {host_name} -c security.nesting={nesting_option} -c security.protection.delete={protection_option}" + + process = Popen(launch_command, shell=True, stdout=PIPE, stderr=PIPE) + output, error = process.communicate() + + if error: + self.show_error_message(f"Fehler beim Starten des Containers {host_name}:\n{error.decode('utf-8')}") + else: + created_hosts.append(host_name) + + if created_hosts: + self.show_info_message(f"Container erfolgreich eingerichtet:\n{', '.join(created_hosts)}") + + def show_hosts(self): + # Hier kannst du die Logik für die Anzeige weiterer Hosts implementieren + # Zum Beispiel ein Popup-Fenster oder eine separate Seite im GUI + + # Reset der Auswahl + self.selected_image.set("") # Zurücksetzen des Auswahlmenüs + self.host_name_var.set("") # Zurücksetzen des Hostnamen-Eingabefelds + self.num_hosts_var.set("") # Zurücksetzen des Felds für die Anzahl der Hosts + self.nesting_checkbox_var.set(False) # Zurücksetzen der Nesting-Checkbox + self.protection_checkbox_var.set(False) # Zurücksetzen der Protection-Checkbox + +if __name__ == "__main__": + root = tk.Tk() + app = LxcLauncherGUI(root) + root.mainloop() diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..ad1a534 --- /dev/null +++ b/gui.py @@ -0,0 +1,69 @@ +import tkinter as tk +from tkinter import ttk +from subprocess import Popen, PIPE +from lxc_ls import LXCInfoGUI +from create import LxcLauncherGUI + +class MyGUI: + def __init__(self, root): + self.root = root + self.root.title("Zwei Ansichten") + self.root.geometry("800x400") # Setze die Fenstergröße auf 800x400 + + menubar = tk.Menu(self.root) + self.root.config(menu=menubar) + + menu1 = tk.Menu(menubar, tearoff=0) + menu1.add_command(label="lxc list", command=self.lxc_list) + menu1.add_command(label="lxc create", command=self.lxc_create) + menubar.add_cascade(label="LXC Manage", menu=menu1) + + menu2 = tk.Menu(menubar, tearoff=0) + menu2.add_command(label="Menüpunkt 2.1", command=self.menu_item2_1) + menu2.add_command(label="Menüpunkt 2.2", command=self.menu_item2_2) + menubar.add_cascade(label="Menü 2", menu=menu2) + + menu3 = tk.Menu(menubar, tearoff=0) + menu3.add_command(label="Menüpunkt 3.1", command=self.menu_item3_1) + menu3.add_command(label="Menüpunkt 3.2", command=self.menu_item3_2) + menubar.add_cascade(label="Menü 3", menu=menu3) + + self.frame1 = ttk.Frame(self.root, style='Green.TFrame') + self.frame1.place(relx=0, rely=0, relwidth=1, relheight=0.4) + + frame2 = ttk.Frame(self.root, style='Yellow.TFrame') + frame2.place(relx=0, rely=0.4, relwidth=1, relheight=0.6) + + ttk.Separator(self.root, orient='horizontal').place(relx=0, rely=0.4, relwidth=1) + + self.root.style = ttk.Style() + self.root.style.configure('Green.TFrame', background='#aaffaa') + self.root.style.configure('Yellow.TFrame', background='#ffffaa') + + def lxc_list(self): + for widget in self.frame1.winfo_children(): + widget.destroy() + lxc_info_app = LXCInfoGUI(self.frame1) + + def lxc_create(self): + # Hier wird das LXC-Erstellungsfenster geöffnet + create_window = tk.Toplevel(self.root) + create_window.title("LXC Launcher") + create_app = LxcLauncherGUI(create_window) + + def menu_item2_1(self): + print("Menüpunkt 2.1 wurde ausgewählt.") + + def menu_item2_2(self): + print("Menüpunkt 2.2 wurde ausgewählt.") + + def menu_item3_1(self): + print("Menüpunkt 3.1 wurde ausgewählt.") + + def menu_item3_2(self): + print("Menüpunkt 3.2 wurde ausgewählt.") + +if __name__ == "__main__": + root = tk.Tk() + app = MyGUI(root) + root.mainloop() diff --git a/lxc_ls.py b/lxc_ls.py new file mode 100644 index 0000000..90f3d30 --- /dev/null +++ b/lxc_ls.py @@ -0,0 +1,161 @@ +import tkinter as tk +from tkinter import ttk, messagebox +import subprocess +import json + +class LXCInfoGUI: + def __init__(self, parent): + self.parent = parent + self.column_width = 120 + columns = ('Host Name', 'Pool', 'IP Address', 'Nesting / Protect', 'OS', 'Status') + self.tree = ttk.Treeview(self.parent, columns=columns, show='headings') + + for col in columns: + self.tree.heading(col, text=col, command=lambda c=col: self.column_sort(c, False)) + self.tree.column(col, width=self.column_width, anchor='w') + + self.tree.pack(padx=10, pady=10) + + self.tree.bind("", self.on_tree_double_click) + + self.refresh_button = ttk.Button(self.parent, text='Aktualisieren', command=self.refresh_info) + self.refresh_button.pack(pady=10) + + self.parent.after(10000, self.auto_refresh_info) + + self.refresh_info() + + self.context_menu = tk.Menu(self.parent, tearoff=0) + self.configure_context_menu() + + self.tree.bind("", self.show_context_menu) + + def configure_context_menu(self): + menu_items = [ + ("Start", self.start_container), + ("Stoppen", self.stop_container), + ("Löschen", self.delete_container), + None, # Separator + ("ProtecNO", self.set_protection_no), + ("ProtectYES", self.set_protection_yes) + ] + + for item in menu_items: + if item: + label, command = item + self.context_menu.add_command(label=label, command=command) + else: + self.context_menu.add_separator() + + def show_context_menu(self, event): + self.context_menu.post(event.x_root, event.y_root) + + def run_command(self, command): + try: + result = subprocess.run(command, stdout=subprocess.PIPE, text=True, shell=True, check=True) + return result.stdout.strip() + except subprocess.CalledProcessError as e: + print(f"Fehler beim Ausführen des Befehls: {e}") + return "" + + def get_lxc_info(self): + command_output = self.run_command( + "lxc list -f json | jq -cr '(.[] | [.name, .expanded_devices.root.pool, " + "(.state.network.eth0.addresses // [] | map(select(.family == \"inet\").address) | join(\", \")), " + "(.config.\"security.nesting\" // false), (.config.\"security.protection.delete\" // false), " + ".config.\"image.os\", .config.\"image.release\", .state.status])'" + ) + try: + lxc_info = [json.loads(line) for line in command_output.splitlines()] + lxc_info.sort(key=lambda x: x[7] != 'Stopped') + return lxc_info + except json.JSONDecodeError as e: + print(f"Fehler beim Dekodieren von JSON: {e}") + return [] + + def refresh_info(self): + for item in self.tree.get_children(): + self.tree.delete(item) + + lxc_info = self.get_lxc_info() + + self.tree.tag_configure('oddrow', background='#f0f0f0') + self.tree.tag_configure('evenrow', background='white') + + for i, container_info in enumerate(lxc_info): + container_name, pool, ip_address, nesting, protection, os, release, status = container_info + tags = ('oddrow', 'evenrow')[i % 2] + self.tree.insert('', 'end', values=(container_name, pool, ip_address, f"{nesting}/{protection}", + f"{os} {release}", status), tags=tags) + + def auto_refresh_info(self): + self.refresh_info() + self.parent.after(10000, self.auto_refresh_info) + + def on_tree_double_click(self, event): + item = self.tree.selection()[0] + container_name = self.tree.item(item, 'values')[0] + status = self.tree.item(item, 'values')[5] + + if status == 'Running': + response = messagebox.askyesno("Container stoppen", f"Soll der Container '{container_name}' gestoppt werden?") + if response: + self.run_command(f"lxc stop {container_name}") + elif status == 'Stopped': + response = messagebox.askyesno("Container starten", f"Soll der Container '{container_name}' gestartet werden?") + if response: + self.run_command(f"lxc start {container_name}") + + def start_container(self): + item = self.tree.selection()[0] + container_name = self.tree.item(item, 'values')[0] + response = messagebox.askyesno("Container starten", f"Soll der Container '{container_name}' gestartet werden?") + if response: + self.run_command(f"lxc start {container_name}") + + def stop_container(self): + item = self.tree.selection()[0] + container_name = self.tree.item(item, 'values')[0] + response = messagebox.askyesno("Container stoppen", f"Soll der Container '{container_name}' gestoppt werden?") + if response: + self.run_command(f"lxc stop {container_name}") + + def delete_container(self): + item = self.tree.selection()[0] + container_name = self.tree.item(item, 'values')[0] + status = self.tree.item(item, 'values')[5] + + if status == 'Running': + response = messagebox.askyesno("Container löschen", f"Der Container '{container_name}' ist noch aktiv. Möchten Sie ihn trotzdem löschen?") + if response: + self.run_command(f"lxc stop {container_name}") + self.run_command(f"lxc delete {container_name}") + else: + response = messagebox.askyesno("Container löschen", f"Soll der Container '{container_name}' gelöscht werden?") + if response: + self.run_command(f"lxc delete {container_name}") + + def set_protection_no(self): + self.set_protection(False) + + def set_protection_yes(self): + self.set_protection(True) + + def set_protection(self, enable_protection): + item = self.tree.selection()[0] + container_name = self.tree.item(item, 'values')[0] + + protection_setting = "true" if enable_protection else "false" + self.run_command(f"lxc config set {container_name} security.protection.delete={protection_setting}") + + def column_sort(self, col, reverse): + l = [(self.tree.set(k, col), k) for k in self.tree.get_children('')] + l.sort(reverse=reverse) + + for index, (val, k) in enumerate(l): + self.tree.move(k, '', index) + +if __name__ == "__main__": + root = tk.Tk() + app = LXCInfoGUI(root) + root.mainloop()