#!.venv/bin/python """ Script per prelevare dati da MySQL e inviare comandi SITE FTP """ import logging import sys from ftplib import FTP import mysql.connector from utils.config import users_loader as setting from utils.database.connection import connetti_db # Configurazione logging logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") logger = logging.getLogger(__name__) # Configurazione server FTP FTP_CONFIG = {"host": "localhost", "user": "admin", "password": "batt1l0", "port": 2121} def connect_ftp() -> FTP: """ Establishes a connection to the FTP server using the predefined configuration. Returns: FTP: An active FTP connection object. """ try: ftp = FTP() ftp.connect(FTP_CONFIG["host"], FTP_CONFIG["port"]) ftp.login(FTP_CONFIG["user"], FTP_CONFIG["password"]) logger.info("Connessione FTP stabilita") return ftp except Exception as e: # pylint: disable=broad-except logger.error("Errore connessione FTP: %s", e) sys.exit(1) def fetch_data_from_db(connection: mysql.connector.MySQLConnection) -> list[tuple]: """ Fetches username and password data from the 'ftp_accounts' table in the database. Args: connection (mysql.connector.MySQLConnection): The database connection object. Returns: List[Tuple]: A list of tuples, where each tuple contains (username, password). """ try: cursor = connection.cursor() # Modifica questa query secondo le tue esigenze query = """ SELECT username, password FROM ase_lar.ftp_accounts """ cursor.execute(query) results = cursor.fetchall() logger.info("Prelevate %s righe dal database", len(results)) return results except mysql.connector.Error as e: logger.error("Errore query database: %s", e) return [] finally: cursor.close() def fetch_existing_users(connection: mysql.connector.MySQLConnection) -> dict[str, tuple]: """ Fetches existing FTP users from virtusers table. Args: connection (mysql.connector.MySQLConnection): The database connection object. Returns: dict: Dictionary mapping username to (is_enabled, has_matching_password). is_enabled is True if disabled_at is NULL. """ try: cursor = connection.cursor() query = """ SELECT ftpuser, disabled_at FROM ase_lar.virtusers """ cursor.execute(query) results = cursor.fetchall() # Create dictionary: username -> is_enabled users_dict = {username: (disabled_at is None) for username, disabled_at in results} logger.info("Trovati %s utenti esistenti in virtusers", len(users_dict)) return users_dict except mysql.connector.Error as e: logger.error("Errore query database virtusers: %s", e) return {} finally: cursor.close() def send_site_command(ftp: FTP, command: str) -> bool: """ Sends a SITE command to the FTP server. Args: ftp (FTP): The FTP connection object. command (str): The SITE command string to send (e.g., "ADDU username password"). Returns: bool: True if the command was sent successfully, False otherwise. """ try: # Il comando SITE viene inviato usando sendcmd response = ftp.sendcmd(f"SITE {command}") logger.info("Comando SITE %s inviato. Risposta: %s", command, response) return True except Exception as e: # pylint: disable=broad-except logger.error("Errore invio comando SITE %s: %s", command, e) return False def main(): """ Main function to connect to the database, fetch FTP user data, and synchronize users to FTP server. This function is idempotent - it can be run multiple times safely: - If user exists and is enabled: skips - If user exists but is disabled: enables it (SITE ENAU) - If user doesn't exist: creates it (SITE ADDU) """ logger.info("Avvio script caricamento utenti FTP (idempotente)") cfg = setting.Config() # Connessioni db_connection = connetti_db(cfg) ftp_connection = connect_ftp() try: # Preleva utenti da sincronizzare users_to_sync = fetch_data_from_db(db_connection) if not users_to_sync: logger.warning("Nessun utente da sincronizzare nel database ftp_accounts") return # Preleva utenti già esistenti existing_users = fetch_existing_users(db_connection) added_count = 0 enabled_count = 0 skipped_count = 0 error_count = 0 # Processa ogni utente for row in users_to_sync: username, password = row if username in existing_users: is_enabled = existing_users[username] if is_enabled: # Utente già esiste ed è abilitato - skip logger.info("Utente %s già esiste ed è abilitato - skip", username) skipped_count += 1 else: # Utente esiste ma è disabilitato - riabilita logger.info("Utente %s esiste ma è disabilitato - riabilito con SITE ENAU", username) ftp_site_command = f"enau {username}" if send_site_command(ftp_connection, ftp_site_command): enabled_count += 1 else: error_count += 1 else: # Utente non esiste - crea logger.info("Utente %s non esiste - creazione con SITE ADDU", username) ftp_site_command = f"addu {username} {password}" if send_site_command(ftp_connection, ftp_site_command): added_count += 1 else: error_count += 1 logger.info( "Elaborazione completata. Aggiunti: %s, Riabilitati: %s, Saltati: %s, Errori: %s", added_count, enabled_count, skipped_count, error_count ) except Exception as e: # pylint: disable=broad-except logger.error("Errore generale: %s", e) finally: # Chiudi connessioni try: ftp_connection.quit() logger.info("Connessione FTP chiusa") except Exception as e: # pylint: disable=broad-except logger.error("Errore chiusura connessione FTP: %s", e) try: db_connection.close() logger.info("Connessione MySQL chiusa") except Exception as e: # pylint: disable=broad-except logger.error("Errore chiusura connessione MySQL: %s", e) if __name__ == "__main__": main()