162 lines
6.4 KiB
Python
162 lines
6.4 KiB
Python
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("<Double-1>", 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("<Button-3>", 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()
|