"""Import des IPs SANEF (export iTop 'Interface réseau') → table server_ips. Usage: python tools/import_sanef_ips.py """ import csv import os import argparse from sqlalchemy import create_engine, text DATABASE_URL = os.getenv("DATABASE_URL_DEMO") or os.getenv("DATABASE_URL") \ or "postgresql://patchcenter:PatchCenter2026!@localhost:5432/patchcenter_demo" def main(): parser = argparse.ArgumentParser() parser.add_argument("csv_path") parser.add_argument("--replace", action="store_true", help="Vider server_ips avant l'import") args = parser.parse_args() engine = create_engine(DATABASE_URL) print(f"[INFO] DB: {DATABASE_URL.split('@')[-1]}") print(f"[INFO] CSV: {args.csv_path}") with open(args.csv_path, "r", encoding="utf-8-sig", newline="") as f: reader = csv.DictReader(f) rows = list(reader) print(f"[INFO] {len(rows)} lignes") conn = engine.connect().execution_options(isolation_level="AUTOCOMMIT") if args.replace: conn.execute(text("DELETE FROM server_ips")) print("[OK] server_ips vidé") linked = 0 missing = 0 skipped = 0 for r in rows: # Hostname peut venir de Machine virtuelle / Serveur / Nom selon l'export iTop vm_name = (r.get("Machine virtuelle->Nom") or r.get("Machine virtuelle") or r.get("Serveur->Nom") or r.get("Serveur") or r.get("Système->Nom") or r.get("Nom") or "").strip() if vm_name: vm_name = vm_name.split(".")[0].lower() ip = (r.get("Adresse IP") or r.get("IP") or "").strip() gw = (r.get("Passerelle") or "").strip() or None mask = (r.get("Masque de sous réseau") or "").strip() or None vrf = (r.get("VRF") or "").strip() or None if not vm_name or not ip: skipped += 1 continue srv = conn.execute(text("SELECT id FROM servers WHERE hostname=:h"), {"h": vm_name}).fetchone() if not srv: missing += 1 continue try: existing = conn.execute(text( "SELECT id FROM server_ips WHERE server_id=:sid AND ip_address=CAST(:ip AS inet)" ), {"sid": srv.id, "ip": ip}).fetchone() if existing: skipped += 1 continue desc_parts = [] if vrf: desc_parts.append(f"VRF={vrf}") if gw: desc_parts.append(f"GW={gw}") if mask: desc_parts.append(f"MASK={mask}") desc = " ".join(desc_parts)[:200] or None conn.execute(text(""" INSERT INTO server_ips (server_id, ip_address, ip_type, description) VALUES (:sid, CAST(:ip AS inet), 'primary', :d) """), {"sid": srv.id, "ip": ip, "d": desc}) linked += 1 except Exception as e: print(f" [ERR] {vm_name} {ip}: {str(e)[:150]}") skipped += 1 conn.close() print(f"[DONE] Liés: {linked} | Hostname introuvable: {missing} | Ignoré: {skipped}") if __name__ == "__main__": main()