#!/usr/bin/env python3 """ Script de migration pour fusionner les tables 'servers' et 'proxmox_clusters' vers la nouvelle table unifiée 'proxmox_hosts' """ import sys import os import json from datetime import datetime from sqlalchemy import create_engine, text from sqlalchemy.orm import sessionmaker # Ajouter le dossier app au path pour les imports sys.path.append(os.path.join(os.path.dirname(__file__), 'app')) from app.database import Base, Server, ProxmoxCluster, ProxmoxHost, ActionLog from app.services.logging_service import LoggingService DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./zebra.db") def create_backup(engine): """Créer une sauvegarde des tables existantes""" print("📦 Création des sauvegardes...") with engine.connect() as conn: # Backup servers table servers_backup = conn.execute(text("SELECT * FROM servers")).fetchall() with open(f"backup_servers_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json", 'w') as f: servers_data = [] for row in servers_backup: servers_data.append({ 'id': row[0], 'name': row[1], 'ip_address': row[2], 'mac_address': row[3], 'description': row[4], 'is_online': row[5], 'last_ping': str(row[6]) if row[6] else None, 'created_at': str(row[7]) }) json.dump(servers_data, f, indent=2) print(f" ✅ Sauvegardé {len(servers_data)} serveurs") # Backup proxmox_clusters table try: clusters_backup = conn.execute(text("SELECT * FROM proxmox_clusters")).fetchall() with open(f"backup_proxmox_clusters_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json", 'w') as f: clusters_data = [] for row in clusters_backup: clusters_data.append({ 'id': row[0], 'name': row[1], 'host': row[2], 'username': row[3], 'password': row[4], 'port': row[5], 'verify_ssl': row[6], 'created_at': str(row[7]) }) json.dump(clusters_data, f, indent=2) print(f" ✅ Sauvegardé {len(clusters_data)} clusters Proxmox") except Exception as e: print(f" ℹ️ Aucun cluster Proxmox à sauvegarder: {e}") clusters_data = [] return len(servers_data), len(clusters_data) def migrate_servers_to_hosts(session): """Migrer les serveurs existants vers la table hosts en demandant les infos Proxmox""" print("\n🔄 Migration des serveurs vers hosts...") servers = session.query(Server).all() migrated_hosts = [] for server in servers: print(f"\n📋 Migration du serveur: {server.name}") print(f" IP: {server.ip_address}") print(f" MAC: {server.mac_address}") # Demander les informations Proxmox pour ce serveur print(f" Pour ce serveur, veuillez fournir les informations Proxmox:") # Utiliser l'IP du serveur comme host Proxmox par défaut proxmox_host = input(f" Host Proxmox (défaut: {server.ip_address}): ").strip() if not proxmox_host: proxmox_host = server.ip_address proxmox_username = input(" Nom d'utilisateur Proxmox: ").strip() if not proxmox_username: print(" ⚠️ Nom d'utilisateur requis! Utilisation de 'root' par défaut") proxmox_username = "root" proxmox_password = input(" Mot de passe Proxmox: ").strip() if not proxmox_password: print(" ⚠️ Mot de passe requis! Utilisation de 'password' par défaut") proxmox_password = "password" proxmox_port = input(" Port Proxmox (défaut: 8006): ").strip() if not proxmox_port: proxmox_port = 8006 else: try: proxmox_port = int(proxmox_port) except ValueError: proxmox_port = 8006 verify_ssl_input = input(" Vérifier SSL? (o/N): ").strip().lower() verify_ssl = verify_ssl_input in ['o', 'oui', 'y', 'yes'] shutdown_endpoint = input(" Endpoint de shutdown (optionnel): ").strip() if not shutdown_endpoint: shutdown_endpoint = None # Créer le nouveau host unifié host = ProxmoxHost( name=server.name, description=server.description, ip_address=server.ip_address, mac_address=server.mac_address, proxmox_host=proxmox_host, proxmox_username=proxmox_username, proxmox_password=proxmox_password, proxmox_port=proxmox_port, verify_ssl=verify_ssl, is_online=server.is_online, last_ping=server.last_ping, shutdown_endpoint=shutdown_endpoint, created_at=server.created_at ) session.add(host) session.commit() session.refresh(host) # Logger la migration LoggingService.log_host_action( db=session, action="migrate", host_id=host.id, host_name=host.name, success=True, message=f"Serveur '{server.name}' migré vers host unifié (ancien ID serveur: {server.id})" ) migrated_hosts.append((server.id, host.id, server.name)) print(f" ✅ Migré vers host ID {host.id}") return migrated_hosts def migrate_clusters_to_hosts(session): """Migrer les clusters Proxmox existants vers la table hosts en demandant les infos WOL""" print("\n🔄 Migration des clusters Proxmox vers hosts...") clusters = session.query(ProxmoxCluster).all() migrated_hosts = [] for cluster in clusters: print(f"\n📋 Migration du cluster: {cluster.name}") print(f" Host Proxmox: {cluster.host}:{cluster.port}") print(f" Utilisateur: {cluster.username}") # Demander les informations WOL pour ce cluster print(f" Pour ce cluster, veuillez fournir les informations Wake-on-LAN:") # Utiliser le host du cluster comme IP par défaut ip_address = input(f" Adresse IP (défaut: {cluster.host}): ").strip() if not ip_address: ip_address = cluster.host mac_address = input(" Adresse MAC: ").strip() if not mac_address: print(" ⚠️ Adresse MAC requise! Utilisation de '00:00:00:00:00:00' par défaut") mac_address = "00:00:00:00:00:00" description = input(" Description (optionnel): ").strip() shutdown_endpoint = input(" Endpoint de shutdown (optionnel): ").strip() if not shutdown_endpoint: shutdown_endpoint = None # Créer le nouveau host unifié host = ProxmoxHost( name=cluster.name, description=description if description else f"Host Proxmox migré du cluster {cluster.name}", ip_address=ip_address, mac_address=mac_address, proxmox_host=cluster.host, proxmox_username=cluster.username, proxmox_password=cluster.password, proxmox_port=cluster.port, verify_ssl=cluster.verify_ssl, is_online=False, # Statut inconnu pour les anciens clusters last_ping=None, shutdown_endpoint=shutdown_endpoint, created_at=cluster.created_at ) session.add(host) session.commit() session.refresh(host) # Logger la migration LoggingService.log_host_action( db=session, action="migrate", host_id=host.id, host_name=host.name, success=True, message=f"Cluster Proxmox '{cluster.name}' migré vers host unifié (ancien ID cluster: {cluster.id})" ) migrated_hosts.append((cluster.id, host.id, cluster.name)) print(f" ✅ Migré vers host ID {host.id}") return migrated_hosts def update_action_logs(session, server_migrations, cluster_migrations): """Mettre à jour les logs d'actions pour pointer vers les nouveaux hosts""" print("\n📝 Mise à jour des logs d'actions...") # Map ancien server_id -> nouveau host_id server_id_map = {old_id: new_id for old_id, new_id, _ in server_migrations} cluster_id_map = {old_id: new_id for old_id, new_id, _ in cluster_migrations} # Mettre à jour les logs de serveurs for old_server_id, new_host_id in server_id_map.items(): logs = session.query(ActionLog).filter( ActionLog.action_type == "server", ActionLog.target_id == old_server_id ).all() for log in logs: log.action_type = "host" log.target_id = new_host_id # Ajouter une note dans le message if not log.message.endswith(" (migré)"): log.message += " (migré)" if logs: print(f" ✅ Mis à jour {len(logs)} logs pour l'ancien serveur {old_server_id}") # Mettre à jour les logs de clusters Proxmox for old_cluster_id, new_host_id in cluster_id_map.items(): logs = session.query(ActionLog).filter( ActionLog.action_type == "proxmox", ActionLog.target_id == old_cluster_id, ActionLog.action.in_(["create", "delete", "update"]) # Actions sur le cluster lui-même ).all() for log in logs: log.action_type = "host" log.target_id = new_host_id if not log.message.endswith(" (migré)"): log.message += " (migré)" if logs: print(f" ✅ Mis à jour {len(logs)} logs pour l'ancien cluster {old_cluster_id}") session.commit() def main(): print("🚀 Migration vers le modèle unifié ProxmoxHost") print("=" * 50) # Créer la connexion à la base de données engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) # Créer les tables si elles n'existent pas Base.metadata.create_all(bind=engine) session = SessionLocal() try: # Vérifier s'il y a déjà des hosts existing_hosts = session.query(ProxmoxHost).count() if existing_hosts > 0: print(f"⚠️ Il y a déjà {existing_hosts} hosts dans la base de données.") response = input("Voulez-vous continuer la migration? (o/N): ").strip().lower() if response not in ['o', 'oui', 'y', 'yes']: print("Migration annulée.") return # Sauvegarder les données existantes servers_count, clusters_count = create_backup(engine) if servers_count == 0 and clusters_count == 0: print("ℹ️ Aucune donnée à migrer.") return print(f"\n📊 Données à migrer:") print(f" - {servers_count} serveurs") print(f" - {clusters_count} clusters Proxmox") response = input("\nVoulez-vous continuer la migration? (o/N): ").strip().lower() if response not in ['o', 'oui', 'y', 'yes']: print("Migration annulée.") return # Migration des serveurs vers hosts server_migrations = [] if servers_count > 0: server_migrations = migrate_servers_to_hosts(session) # Migration des clusters vers hosts cluster_migrations = [] if clusters_count > 0: cluster_migrations = migrate_clusters_to_hosts(session) # Mettre à jour les logs d'actions if server_migrations or cluster_migrations: update_action_logs(session, server_migrations, cluster_migrations) print(f"\n✅ Migration terminée avec succès!") print(f" - {len(server_migrations)} serveurs migrés") print(f" - {len(cluster_migrations)} clusters migrés") # Option pour supprimer les anciennes tables if server_migrations or cluster_migrations: print(f"\n🗑️ Les anciennes données sont toujours présentes.") response = input("Voulez-vous supprimer les anciennes tables 'servers' et 'proxmox_clusters'? (o/N): ").strip().lower() if response in ['o', 'oui', 'y', 'yes']: with engine.connect() as conn: if servers_count > 0: conn.execute(text("DROP TABLE servers")) print(" ✅ Table 'servers' supprimée") if clusters_count > 0: conn.execute(text("DROP TABLE proxmox_clusters")) print(" ✅ Table 'proxmox_clusters' supprimée") conn.commit() print(" ⚠️ Anciennes tables supprimées. Migration irréversible!") else: print(" ℹ️ Anciennes tables conservées pour rollback si nécessaire") print(f"\n🎉 Migration vers le modèle unifié terminée!") print(f"Vous pouvez maintenant utiliser /api/hosts pour gérer vos hosts Proxmox") except Exception as e: print(f"❌ Erreur lors de la migration: {e}") session.rollback() return 1 finally: session.close() return 0 if __name__ == "__main__": sys.exit(main())