diff --git a/linux/backups_manage/README.md b/linux/backups_manage/README.md new file mode 100644 index 0000000..fd17eb0 --- /dev/null +++ b/linux/backups_manage/README.md @@ -0,0 +1,52 @@ +# Script de Gerenciamento de Backup + +Script para gerenciamento e rotação de backups em servidores FTP. + +## Funcionalidades + +- **Varredura Dinâmica**: Detecta automaticamente novas pastas de usuários em `/home/ftpuser`. +- **Organização Plana**: Arquivos antigos são movidos para `antigos/usuario/`, preservando a estrutura original. +- **Limpeza Automática**: + - Arquivos com mais de **15 dias** são movidos para a pasta de arquivamento. + - Arquivos na pasta de arquivamento com mais de **60 dias** são excluídos permanentemente. + - Logs de execução com mais de **30 dias** são removidos. +- **Logs Detalhados**: Gera logs diários em `/home/ftpuser/logs/`. + +## Instalação e Uso + +### 1. Dependências +O script utiliza apenas bibliotecas padrão do Python 3. Certifique-se de ter o Python 3 instalado: + +```bash +sudo apt update +sudo apt install python3 +``` + + +### 2. Configuração do Cron +Adicione a seguinte linha ao crontab (`crontab -e` ou `/etc/crontab`) para executar o script diariamente (ex: meia-noite): + +```bash +0 0 * * * root /usr/bin/python3 /usr/local/sbin/manage_backups.py +``` + +### 3. Parametrização e Filtros +As configurações podem ser ajustadas no topo do arquivo `manage_backups.py`: + +**Retenção:** +```python +DIAS_MOVER = 15 # Dias antes de mover para 'antigos' +DIAS_EXCLUIR_ANTIGOS = 60 # Dias antes de excluir permanentemente de 'antigos' +DIAS_RETENCAO_LOGS = 30 # Dias de retenção dos logs +``` + +**Filtros (Ignorar Pastas e Arquivos):** +Você pode configurar pastas e extensões que o script deve **ignorar completamente** (não mover nem excluir): + +```python +# Pastas para ignorar na varredura +DIRETORIOS_IGNORAR = ['antigos', 'logs', 'temp'] + +# Extensões para ignorar (ex: executáveis, isos) +EXTENSOES_IGNORAR = ['.exe', '.iso'] +``` \ No newline at end of file diff --git a/linux/backups_manage/manage_backups.py b/linux/backups_manage/manage_backups.py new file mode 100644 index 0000000..0bde107 --- /dev/null +++ b/linux/backups_manage/manage_backups.py @@ -0,0 +1,224 @@ +# Manage Backups V2.0 by Teccnia +# https://teccnia.com.br + + +#!/usr/bin/env python3 +import os +import shutil +import time +import logging +from datetime import datetime, timedelta +from pathlib import Path + +# ========================================== +# CONFIGURAÇÃO +# ========================================== + +# Diretório base onde estão as pastas dos usuários FTP +DIRETORIO_BASE = "/home/ftpuser" + +# Nome das pastas para ignorar na varredura de usuários +DIRETORIOS_IGNORAR = ['antigos', 'logs'] + +# Extensões para ignorar (não serão movidas nem deletadas) +# Exemplo: ['.exe', '.iso'] +EXTENSOES_IGNORAR = ['.exe', '.iso'] + +# Configurações de Retenção +DIAS_MOVER = 15 # Arquivos com mais de X dias são movidos para 'antigos' +DIAS_EXCLUIR_ANTIGOS = 60 # Pastas em 'antigos' com mais de Y dias são excluídas +DIAS_RETENCAO_LOGS = 30 # Logs com mais de Z dias são excluídos + +# ======= NÃO ALTERAR A PARTIR DESTE PONTO!!! ======= + +# Nomes de diretórios de sistema +NOME_DIR_ANTIGOS = "antigos" +NOME_DIR_LOGS = "logs" + +# Caminhos completos (serão definidos dinamicamente se baseados no DIRETORIO_BASE) +DIR_LOGS = os.path.join(DIRETORIO_BASE, NOME_DIR_LOGS) +DIR_ANTIGOS = os.path.join(DIRETORIO_BASE, NOME_DIR_ANTIGOS) + +# Data atual para organização +HOJE = datetime.now() +DATA_HOJE_STR = HOJE.strftime('%Y-%m-%d') +DATA_LOG_STR = HOJE.strftime('%d%m%Y') + +# ========================================== +# CONFIGURAÇÃO DE LOGS +# ========================================== +def configurar_logger(): + if not os.path.exists(DIR_LOGS): + try: + os.makedirs(DIR_LOGS) + except Exception as e: + print(f"ERRO CRÍTICO: Não foi possível criar diretório de logs {DIR_LOGS}: {e}") + exit(1) + + arquivo_log = os.path.join(DIR_LOGS, f"{DATA_LOG_STR}.log") + + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler(arquivo_log), + logging.StreamHandler() + ] + ) + return logging.getLogger() + +# ========================================== +# FUNÇÕES +# ========================================== + +def idade_arquivo_dias(caminho_arquivo): + """Retorna a idade do arquivo em dias.""" + timestamp_arquivo = os.path.getmtime(caminho_arquivo) + data_arquivo = datetime.fromtimestamp(timestamp_arquivo) + diferenca = HOJE - data_arquivo + return diferenca.days + +def listar_subdiretorios_usuarios(logger): + """Retorna lista de subdiretórios de usuários, ignorando pastas de sistema.""" + subdiretorios = [] + if not os.path.exists(DIRETORIO_BASE): + logger.error(f"Diretório base não existe: {DIRETORIO_BASE}") + return [] + + try: + itens = os.listdir(DIRETORIO_BASE) + for item in itens: + caminho_completo = os.path.join(DIRETORIO_BASE, item) + if os.path.isdir(caminho_completo): + if item not in DIRETORIOS_IGNORAR: + subdiretorios.append(item) + except Exception as e: + logger.error(f"Erro ao listar diretório base: {e}") + + return subdiretorios + +def processar_arquivos_antigos(usuario, logger): + """Move arquivos antigos do diretório do usuário para a pasta de arquivamento.""" + origem = os.path.join(DIRETORIO_BASE, usuario) + destino_base = os.path.join(DIR_ANTIGOS, usuario) + + if not os.path.exists(origem): + logger.warning(f"Diretório de origem não encontrado: {origem}") + return + + arquivos_movidos = 0 + try: + destino_criado = False + + for arquivo in os.listdir(origem): + caminho_arquivo = os.path.join(origem, arquivo) + + if not os.path.isfile(caminho_arquivo): + continue + + _, ext = os.path.splitext(arquivo) + if ext.lower() in [e.lower() for e in EXTENSOES_IGNORAR]: + continue + + idade = idade_arquivo_dias(caminho_arquivo) + + if idade > DIAS_MOVER: + if not destino_criado: + os.makedirs(destino_base, exist_ok=True) + destino_criado = True + + shutil.move(caminho_arquivo, os.path.join(destino_base, arquivo)) + logger.info(f"Movido: {usuario}/{arquivo} ({idade} dias) -> {destino_base}") + arquivos_movidos += 1 + + except Exception as e: + logger.error(f"Erro ao processar usuário {usuario}: {e}") + + if arquivos_movidos > 0: + logger.info(f"Usuário {usuario}: {arquivos_movidos} arquivos movidos.") + +def limpar_arquivos_antigos(logger): + """Remove ARQUIVOS antigos em 'antigos' recursivamente.""" + if not os.path.exists(DIR_ANTIGOS): + return + + logger.info("Verificando limpeza de backups antigos (estrutura plana)...") + arquivos_removidos = 0 + + try: + for root, dirs, files in os.walk(DIR_ANTIGOS): + for file in files: + caminho_arquivo = os.path.join(root, file) + + # Verificar extensão ignorada + _, ext = os.path.splitext(file) + if ext.lower() in [e.lower() for e in EXTENSOES_IGNORAR]: + continue + + try: + idade = idade_arquivo_dias(caminho_arquivo) + + if idade > DIAS_EXCLUIR_ANTIGOS: + os.remove(caminho_arquivo) + logger.info(f"Removido backup antigo: {caminho_arquivo} ({idade} dias)") + arquivos_removidos += 1 + except Exception as e: + logger.error(f"Erro ao verificar arquivo {caminho_arquivo}: {e}") + + for root, dirs, files in os.walk(DIR_ANTIGOS, topdown=False): + for name in dirs: + caminho_dir = os.path.join(root, name) + if not os.listdir(caminho_dir): # Se estiver vazio + os.rmdir(caminho_dir) + logger.debug(f"Removido diretório vazio: {caminho_dir}") + + except Exception as e: + logger.error(f"Erro ao limpar backups antigos: {e}") + + if arquivos_removidos > 0: + logger.info(f"Total de backups antigos removidos: {arquivos_removidos}") + +def rotacionar_logs(logger): + """Remove logs antigos.""" + if not os.path.exists(DIR_LOGS): + return + + logger.info("Verificando rotação de logs...") + try: + for arquivo in os.listdir(DIR_LOGS): + caminho_arquivo = os.path.join(DIR_LOGS, arquivo) + if os.path.isfile(caminho_arquivo): + idade = idade_arquivo_dias(caminho_arquivo) + if idade > DIAS_RETENCAO_LOGS: + os.remove(caminho_arquivo) + logger.info(f"Log removido: {arquivo} ({idade} dias)") + except Exception as e: + logger.error(f"Erro ao rotacionar logs: {e}") + +# ========================================== +# MAIN +# ========================================== +def main(): + # Inicializa Logger + logger = configurar_logger() + logger.info("=== Iniciando execução do script de backup ===") + logger.info(f"Diretório Base: {DIRETORIO_BASE}") + + # 1. Identificar usuários (subdiretórios) + usuarios = listar_subdiretorios_usuarios(logger) + logger.info(f"Usuários encontrados: {usuarios}") + + # 2. Processar cada usuário + for usuario in usuarios: + processar_arquivos_antigos(usuario, logger) + + # 3. Limpar pastas antigas de retenção + limpar_arquivos_antigos(logger) + + # 4. Rotacionar logs + rotacionar_logs(logger) + + logger.info("=== Execução finalizada com sucesso ===") + +if __name__ == "__main__": + main()