"""Validation screen with auto-refresh status monitoring.""" import subprocess import threading import time import webbrowser import tkinter as tk from tkinter import ttk, messagebox, filedialog from installer.screen import TerminalConsole from installer import screen as ui class ValidateScreenMixin: """Validation screen with auto-refresh status monitoring.""" def create_validate_screen(self): """Create the Validate screen""" frame = self._page_container() self.screens['validate'] = frame # Title title = ttk.Label(frame, text="Validate Deployment", style='Title.TLabel') title.pack(pady=(0, 20)) # Prometheus link prometheus_frame = tk.Frame(frame, bg='white') prometheus_frame.pack(pady=(0, 20)) prometheus_label = tk.Label(prometheus_frame, text="Prometheus: ", bg='white', fg='#6e6e73', font=('Helvetica', 11)) prometheus_label.pack(side='left') prometheus_link = tk.Label(prometheus_frame, text="http://localhost:9090", bg='white', fg='#4a9eff', font=('Helvetica', 11, 'underline'), cursor='hand2') prometheus_link.pack(side='left') prometheus_link.bind('', lambda e: webbrowser.open('http://localhost:9090')) # Status display status_frame = tk.Frame(frame, bg='white') status_frame.pack(fill='both', expand=True, pady=10) status_label = ttk.Label(status_frame, text="Cluster Status", style='Heading.TLabel') status_label.pack(anchor='w', pady=(0, 10)) # Status text area - using TerminalConsole for consistency # Use a background frame to ensure NO borders are visible around the console console_bg = tk.Frame(status_frame, bg='white', highlightthickness=0, bd=0) console_bg.pack(fill='both', expand=True, padx=1, pady=1) self._validation_console = ui.TerminalConsole(console_bg, highlightthickness=0, bd=0) self._validation_console.pack(fill='both', expand=True) self.status_text = self._validation_console.text # Auto-refresh checkbox refresh_frame = tk.Frame(frame, bg='white') refresh_frame.pack(pady=10) self.auto_refresh_var = tk.BooleanVar(value=True) refresh_check = tk.Checkbutton(refresh_frame, text="Auto-refresh every 10 seconds", variable=self.auto_refresh_var, bg='white', fg='#1d1d1f', selectcolor='#f0f0f0', activebackground='white', activeforeground='#1d1d1f', font=('Helvetica', 10), command=self.toggle_auto_refresh) refresh_check.pack(side='left', padx=10) # Manual refresh button refresh_btn = tk.Button(refresh_frame, text="Refresh Now", command=self.refresh_status, bg='#4a9eff', fg='white', activebackground='#3a8eef', highlightbackground='white', font=('Helvetica', 10), padx=15, pady=5, cursor='hand2', relief='flat') refresh_btn.pack(side='left', padx=10) # Start auto-refresh self.refresh_status() self.toggle_auto_refresh() def toggle_auto_refresh(self): """Toggle auto-refresh""" if self.auto_refresh_var.get(): if not self.validation_running: self.validation_running = True self.validation_thread = threading.Thread(target=self.auto_refresh_loop, daemon=True) self.validation_thread.start() else: self.validation_running = False def auto_refresh_loop(self): """Auto-refresh loop""" while self.validation_running: time.sleep(10) if self.validation_running: self.root.after(0, self.refresh_status) def refresh_status(self): """Refresh the cluster status""" def update(): try: full_status = "" # Get k3d cluster list try: cluster_result = subprocess.run(['k3d', 'cluster', 'list'], capture_output=True, text=True, timeout=5) full_status += f"=== k3d Cluster Status ===\n{cluster_result.stdout}\n\n" except Exception as e: full_status += f"=== k3d Cluster Status ===\nError: {str(e)}\n\n" # Get kubectl cnpg status try: namespace = (self.db_namespace.get() or 'default').strip() cmd = self._kubectl_base_cmd() + ['cnpg', 'status', 'prole-db', '-n', namespace] result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) if result.returncode == 0: status_output = result.stdout else: # Try default namespace if prole fails, or just show error status_output = f"Error: {result.stderr}\n\nNote: Make sure kubectl cnpg plugin is installed and cluster exists in '{namespace}' namespace." full_status += f"=== CloudNativePG Status (namespace: {namespace}) ===\n{status_output}\n" except FileNotFoundError: full_status += "=== CloudNativePG Status ===\nError: kubectl not found. Please install dependencies first.\n" except subprocess.TimeoutExpired: full_status += "=== CloudNativePG Status ===\nError: Command timed out\n" except Exception as e: full_status += f"=== CloudNativePG Status ===\nError: {str(e)}\n" full_status += f"\nLast updated: {time.strftime('%Y-%m-%d %H:%M:%S')}" self.status_text.delete('1.0', tk.END) self.status_text.insert('1.0', full_status) except Exception as e: self.status_text.delete('1.0', tk.END) self.status_text.insert('1.0', f"Error: {str(e)}") threading.Thread(target=update, daemon=True).start()