ftp idempotente e istanziabile più volte + logghing su stout x promtail

This commit is contained in:
2025-11-01 15:58:02 +01:00
parent 1d7d33df0b
commit 76094f7641
8 changed files with 132766 additions and 71 deletions

View File

@@ -68,6 +68,38 @@ def fetch_data_from_db(connection: mysql.connector.MySQLConnection) -> list[tupl
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.
@@ -90,9 +122,13 @@ def send_site_command(ftp: FTP, command: str) -> bool:
def main():
"""
Main function to connect to the database, fetch FTP user data, and send SITE ADDU commands to the FTP server.
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")
logger.info("Avvio script caricamento utenti FTP (idempotente)")
cfg = setting.Config()
# Connessioni
@@ -100,32 +136,58 @@ def main():
ftp_connection = connect_ftp()
try:
# Preleva dati dal database
data = fetch_data_from_db(db_connection)
# Preleva utenti da sincronizzare
users_to_sync = fetch_data_from_db(db_connection)
if not data:
logger.warning("Nessun dato trovato nel database")
if not users_to_sync:
logger.warning("Nessun utente da sincronizzare nel database ftp_accounts")
return
success_count = 0
# 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 riga
for row in data:
# Processa ogni utente
for row in users_to_sync:
username, password = row
# Costruisci il comando SITE completo
ftp_site_command = f"addu {username} {password}"
if username in existing_users:
is_enabled = existing_users[username]
logger.info("Sending ftp command: %s", ftp_site_command)
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}"
# Invia comando SITE
if send_site_command(ftp_connection, ftp_site_command):
success_count += 1
if send_site_command(ftp_connection, ftp_site_command):
enabled_count += 1
else:
error_count += 1
else:
error_count += 1
# Utente non esiste - crea
logger.info("Utente %s non esiste - creazione con SITE ADDU", username)
ftp_site_command = f"addu {username} {password}"
logger.info("Elaborazione completata. Successi: %s, Errori: %s", success_count, error_count)
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)