import reflex as rx import subprocess import os import glob import datetime import asyncio # --- PFADE ABSOLUT DEFINIEREN --- # Holt das Verzeichnis, in dem die app.py liegt (test_ansible/app/) APP_DIR = os.path.dirname(os.path.abspath(__file__)) # Geht eine Ebene höher in das Hauptverzeichnis (test_ansible/) BASE_DIR = os.path.dirname(APP_DIR) PLAYBOOK_DIR = os.path.join(BASE_DIR, "playbooks") OUTPUT_DIR = os.path.join(BASE_DIR, "outputs") # Sichere Ordner-Erstellung try: os.makedirs(PLAYBOOK_DIR, exist_ok=True) os.makedirs(OUTPUT_DIR, exist_ok=True) except Exception as e: print(f"Fehler bei der Ordner-Erstellung: {str(e)}") class AppState(rx.State): playbooks: list[str] = [] selected_playbook: str = "" output: str = "" is_running: bool = False error: str = "" # Füge diesen Event-Handler manuell hinzu: def set_selected_playbook(self, val: str): self.selected_playbook = val async def load_playbooks(self): """Lädt alle Playbooks aus dem konfigurierten Ordner.""" try: # --- DIESE PRINTS HIER HINZUFÜGEN --- print(f"[DEBUG] Suche in Ordner: {PLAYBOOK_DIR}") print(f"[DEBUG] Existiert der Ordner? {os.path.exists(PLAYBOOK_DIR)}") playbook_files = glob.glob(os.path.join(PLAYBOOK_DIR, "*.yml")) + \ glob.glob(os.path.join(PLAYBOOK_DIR, "*.yaml")) print(f"[DEBUG] Gefundene Dateien: {playbook_files}") # ------------------------------------ self.playbooks = [os.path.basename(p) for p in playbook_files] if self.playbooks: self.selected_playbook = self.playbooks[0] else: self.selected_playbook = "" self.error = "" except Exception as e: self.error = f"Fehler beim Laden der Playbooks: {str(e)}" async def run_playbook(self): """Führt das ausgewählte Playbook asynchron aus und speichert den Output.""" if not self.selected_playbook: self.error = "Bitte wählen Sie ein Playbook aus." return self.is_running = True self.error = "" self.output = "Wird ausgeführt..." yield # State aktualisieren, bevor die lange Aufgabe startet playbook_path = os.path.join(PLAYBOOK_DIR, self.selected_playbook) timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") output_filename = f"{timestamp}_{self.selected_playbook.replace('.yml', '').replace('.yaml', '')}.txt" output_path = os.path.join(OUTPUT_DIR, output_filename) try: # ansible-playbook Befehl asynchron ausführen proc = await asyncio.create_subprocess_exec( "ansible-playbook", playbook_path, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) # Timeout von 300 Sekunden (5 Minuten) try: stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=300) except asyncio.TimeoutError: proc.kill() await proc.wait() raise asyncio.TimeoutError("Playbook hat das Timeout überschritten.") output_text = stdout.decode() + stderr.decode() self.output = output_text # Output speichern try: with open(output_path, "w", encoding="utf-8") as f: f.write(output_text) except Exception as e: self.error = f"Fehler beim Speichern des Outputs: {str(e)}" if proc.returncode != 0: self.error = f"Playbook wurde mit Fehlercode {proc.returncode} beendet." else: duration = (datetime.datetime.now() - start_time).total_seconds() self.error = "" rx.notify( title="Erfolgreich ausgeführt", description=f"{self.selected_playbook} in {duration:.2f} Sekunden abgeschlossen", status="success", duration=3000 ) except asyncio.TimeoutError as e: self.error = str(e) self.output = "Fehler: Timeout" except Exception as e: self.error = f"Ein Fehler ist aufgetreten: {str(e)}" self.output = "Fehler bei der Ausführung" finally: self.is_running = False yield # State nach Abschluss aktualisieren def index(): return rx.container( rx.heading("Ansible Playbook Runner", size="6"), # Flexiblerer und reaktiverer Aufbau des Dropdowns: rx.select.root( rx.select.trigger(placeholder="Wählen Sie ein Playbook..."), rx.select.content( rx.select.group( rx.foreach( AppState.playbooks, lambda item: rx.select.item(item, value=item) ) ) ), value=AppState.selected_playbook, on_change=AppState.set_selected_playbook, width="100%", ), rx.button( "Ausführen", on_click=AppState.run_playbook, disabled=AppState.is_running, color_scheme="blue", margin_top="1em", ), rx.text( AppState.error, color=rx.cond(AppState.error, "red", "transparent"), margin_top="1em", ), rx.text_area( value=AppState.output, width="100%", height="400px", margin_top="1em", disabled=True, resize="none", ), padding="2em", max_width="800px", margin="auto", ) app = rx.App() app.add_page(index, route="/", on_load=AppState.load_playbooks) if __name__ == "__main__": try: app.run(debug=True) except Exception as e: print(f"Fehler bei der Ausführung: {str(e)}")