212 lines
6.7 KiB
Python
212 lines
6.7 KiB
Python
#!.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()
|