Monitoramento de Discos com Smartctl e Python

11 min de leitura Automação
Monitoramento de Discos com Smartctl e Python

O monitoramento proativo da saúde dos discos rígidos (HDDs) e unidades de estado sólido (SSDs) é uma das responsabilidades mais críticas para qualquer administrador de sistemas. A falha de armazenamento não é apenas um inconveniente; ela representa risco direto à disponibilidade dos serviços, integridade dos dados e continuidade do negócio. Enquanto ferramentas gráficas e soluções enterprise oferecem dashboards completos, a automação via linha de comando com smartctl e scripts em Python proporciona uma camada leve, eficiente e altamente customizável para garantir que alertas sejam disparados antes que o hardware falhe catastróficamente.

Neste tutorial técnico, demonstraremos como integrar a ferramenta smartctl (parte do pacote smartmontools) com um script em Python para criar um sistema de monitoramento contínuo. O objetivo é extrair atributos SMART (Self-Monitoring, Analysis and Reporting Technology), interpretar os dados brutos e gerar alertas baseados em thresholds configuráveis.

1. Preparação do Ambiente e Dependências

Antes de escrever qualquer código, é fundamental garantir que o sistema operacional possua as ferramentas necessárias instaladas. O smartctl comunica-se diretamente com a interface SATA/SCSI/NVMe do disco para ler seus registros internos de saúde.

Instalação do smartmontools

A maioria das distribuições Linux modernas já traz o smartmontools nos repositórios padrão. Execute os comandos abaixo conforme sua distribuição:

# Para sistemas baseados em Debian/Ubuntu
sudo apt update
sudo apt install smartmontools -y

# Para sistemas baseados em RHEL/CentOS/Rocky
sudo yum install smartmontools -y

Após a instalação, verifique se o serviço está ativo e configurado para iniciar com o boot. Em muitos casos, o daemon smartd já monitora os discos em segundo plano, mas para nosso script customizado, executaremos o smartctl sob demanda.

Identificação dos Discos

Utilize o comando lsblk ou fdisk -l para identificar os dispositivos de bloco que você deseja monitorar. Geralmente, eles são nomeados como /dev/sda, /dev/nvme0n1, etc.

lsblk -d -o NAME,TYPE,SIZE,MODEL

Anote os nomes dos dispositivos alvo. Note que é necessário ter privilégios de root (ou usuário com permissões SUDO) para acessar esses dispositivos.

2. Entendendo a Saída do smartctl

Antes de automatizar, é crucial entender o formato de saída da ferramenta. O comando smartctl -a /dev/sdX retorna uma grande quantidade de informações. Para nosso script, focaremos em três áreas principais:

  1. Status Geral: Indicador booleano de se o disco está "PASSED" (Aprovado) ou falhou nos testes self-test.
  2. Atributos Críticos: Valores como Reallocated_Sector_Ct, Current_Pending_Sector e Offline_Uncorrectable.
  3. Temperatura: O atributo de temperatura é vital para prevenir degradação prematura.

Vamos testar a saída em formato JSON, que é muito mais fácil de parsear programaticamente do que o texto bruto padrão:

sudo smartctl -j /dev/sda

A opção -j força a saída em JSON. Isso elimina a necessidade de regex complexas para parsing de texto, reduzindo erros e aumentando a robustez do script.

3. Estruturação do Script Python

Criaremos um script Python modular que utiliza a biblioteca padrão subprocess para chamar o smartctl e a biblioteca json para interpretar os dados. Para simplificar, não utilizaremos frameworks externos pesados.

Bibliotecas Necessárias

O script utilizará apenas módulos padrão do Python 3:

  • subprocess: Para execução de comandos shell.
  • json: Para parsing da saída JSON.
  • logging: Para registrar eventos e erros de forma estruturada.
  • time: Para controle de intervalos entre verificações.

Código Fonte do Monitor

Crie um arquivo chamado disk_monitor.py e insira o seguinte código. Este script é projetado para rodar em loop, verificando a saúde dos discos definidos na lista DISKS_TO_MONITOR.

import subprocess
import json
import logging
import time
import sys

# Configuração de Logging
logging.basicConfig(
    filename='/var/log/disk_monitor.log',
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# Configurações
DISKS_TO_MONITOR = ['/dev/sda', '/dev/nvme0n1']
CHECK_INTERVAL_SECONDS = 300  # Verifica a cada 5 minutos
ALERT_EMAIL = "[email protected]"  # Placeholder para futura integração

# Thresholds de Alerta (Valores ABSOLUTOS ou atributos específicos)
THRESHOLDS = {
    "temperature_max_celsius": 60,
    "critical_warning": 1,  # NVMe specific
    "reallocated_sector_count_threshold": 0  # Se > 0, alerta
}

def get_smart_data(device):
    """
    Executa smartctl e retorna os dados parseados em JSON.
    """
    try:
        # Utiliza sudo para garantir permissão de leitura
        cmd = ["sudo", "smartctl", "-j", device]
        
        # Executa o comando e captura a saída
        result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        
        if result.returncode != 0:
            logging.error(f"Falha ao executar smartctl em {device}: {result.stderr}")
            return None
            
        data = json.loads(result.stdout)
        return data
        
    except Exception as e:
        logging.error(f"Erro inesperado ao obter dados de {device}: {str(e)}")
        return None

def check_nvme_health(data):
    """
    Verifica a saúde específica para discos NVMe.
    """
    alerts = []
    try:
        critical_warning = data.get("nvme_smart_health_info_log", {}).get("critical_warning", 0)
        
        if critical_warning >= THRESHOLDS["critical_warning"]:
            alerts.append(f"ALERTA CRÍTICO NVMe em {device}: Critical Warning flag ativa.")
            
    except KeyError:
        pass
        
    return alerts

def check_ata_health(data, device):
    """
    Verifica a saúde específica para discos SATA/SAS (ATA).
    """
    alerts = []
    
    # 1. Verificar Status Global
    global_health = data.get("smart_status", {}).get("passed", False)
    if not global_health:
        alerts.append(f"FALHA SMART GLOBAL em {device}: O disco passou no auto-teste? Não.")

    # 2. Verificar Atributos Críticos
    attributes = data.get("ata_smart_attributes", {}).get("table", [])
    
    for attr in attributes:
        name = attr.get("name", "")
        value = attr.get("value")
        
        # Foco em setores realocados
        if "Reallocated_Sector_Ct" in name:
            if value > THRESHOLDS["reallocated_sector_count_threshold"]:
                alerts.append(f"ALERTA em {device}: Setores realocados ({name}) = {value}. Isso indica defeitos físicos.")
                
        # Foco em setores pendentes (iminentes falhas)
        if "Current_Pending_Sector" in name:
            if value > 0:
                alerts.append(f"ALERTA em {device}: Setores pendentes ({name}) = {value}. Leitura/Falha iminente.")

    # 3. Verificar Temperatura
    try:
        # A estrutura do JSON varia ligeiramente entre fabricantes, mas geralmente está em temperature_sensor_1
        temp_data = data.get("temperature", {}).get("current_temperature_celsius") or \
                    data.get("data_sets").get(2, {}).get("host_read_recovery") # Fallback genérico
        
        # Tentativa mais robusta de pegar temperatura
        if "temperature" in data:
            temp = data["temperature"].get("current_temperature_celsius", 0)
            if temp > THRESHOLDS["temperature_max_celsius"]:
                alerts.append(f"ALERTA em {device}: Temperatura alta ({temp}°C). Limite é {THRESHOLDS['temperature_max_celsius']}°C.")
    except Exception:
        logging.warning("Não foi possível determinar a temperatura do disco %s", device)

    return alerts

def main():
    logging.info("Iniciando monitoramento de discos...")
    
    while True:
        for device in DISKS_TO_MONITOR:
            print(f"Verificando {device}...")
            data = get_smart_data(device)
            
            if data is None:
                continue
                
            alerts = []
            
            # Verificar se é NVMe ou ATA para usar o verificador correto
            device_type = data.get("device", {}).get("name", "")
            
            if "nvme" in device_type.lower():
                alerts.extend(check_nvme_health(data))
            else:
                alerts.extend(check_ata_health(data, device))
                
            if alerts:
                alert_message = f"\n--- ALERTAS DE DISCO ---\nDisco: {device}\n" + "\n".join(alerts) + "\n------------------------"
                print(alert_message)
                logging.warning(alert_message)
                # Aqui você integraria com seu sistema de notificação (Email, Slack, PagerDuty)
                # send_notification(ALERT_EMAIL, alert_message)
                
        print(f"Aguardando {CHECK_INTERVAL_SECONDS} segundos...")
        time.sleep(CHECK_INTERVAL_SECONDS)

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        logging.info("Monitoramento interrompido pelo usuário.")
        sys.exit(0)

4. Explicação Técnica do Código

O script acima segue uma lógica clara de extração, validação e reporte.

Geração de JSON: A função get_smart_data executa o comando sudo smartctl -j /dev/sdX. O uso de -j é intencional. Sem ele, o smartctl retorna texto formatado para humanos, que requer parsing complexo e frágil (regex). O JSON estruturado permite acessar dados aninhados como data["ata_smart_attributes"]["table"] de forma segura.

Diferenciação de Protocolos: Discos SATA (ATA) e NVMe possuem estruturas de atributos diferentes. O script verifica o nome do dispositivo para decidir qual função de verificação chamar (check_ata_health ou check_nvme_health). Isso é essencial porque atributos como Critical_Warning são exclusivos do modelo NVMe, enquanto Reallocated_Sector_Ct é clássico em SATA.

Thresholds Configuráveis: O dicionário THRESHOLDS no topo do script permite que você ajuste os limites de alerta sem alterar a lógica principal. Por exemplo, se seus discos NVMe tiverem um limite de temperatura diferente, basta atualizar o valor.

Logging: Utilizamos o módulo logging em vez de apenas print(). Isso permite que os logs sejam redirecionados para arquivos ou agregadores centralizados (como ELK Stack ou Graylog) e evita que o script polua o terminal se rodar em background.

5. Execução e Daemonização

Para executar o script manualmente em primeiro plano (para testes):

sudo python3 disk_monitor.py

Observe a saída no terminal e verifique o arquivo /var/log/disk_monitor.log para entradas detalhadas.

Criando um Serviço Systemd

Para garantir que o monitoramento rode persistentemente mesmo após reinicializações, crie um arquivo de serviço systemd. Crie o arquivo /etc/systemd/system/disk-monitor.service:

[Unit]
Description=Disk Health Monitor using Smartctl and Python
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/bin/python3 /opt/scripts/disk_monitor.py
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

Em seguida, ative e inicie o serviço:

sudo systemctl daemon-reload
sudo systemctl enable disk-monitor.service
sudo systemctl start disk-monitor.service
sudo systemctl status disk-monitor.service

Verifique os logs do serviço com journalctl -u disk-monitor.service -f.

6. Integração com Alertas (Próximos Passos)

O script atual registra alertas no log. Para um ambiente de produção, é recomendável integrar notificações reais.

  • Email: Utilize a biblioteca smtplib do Python para enviar emails SMTP diretos.
  • Webhooks: Se sua empresa utiliza Slack, Discord ou Microsoft Teams, crie uma função que envie um POST JSON para o Webhook do canal de #alertas-infra.
  • Sistemas de Monitoramento: Você pode configurar o script para emitir métricas compatíveis com Prometheus (Exposition Format) e permitir que o Grafana visualize a saúde dos discos ao longo do tempo.

7. Boas Práticas e Considerações Finais

Segurança: O script exige sudo. Certifique-se de que as permissões do sudoers estejam configuradas corretamente para permitir a execução apenas do smartctl, e não de qualquer comando, para evitar riscos de segurança.

Falsos Positivos: Alguns discos podem reportar temperaturas altas ocasionalmente devido a picos de carga da CPU. Implemente lógica de "debounce" (ex: alertar apenas se a temperatura alta persistir por 3 verificações consecutivas) para evitar alarmes desnecessários.

NVMe vs SATA: Lembre-se que SSDs NVMe têm mecanismos de desgaste diferentes. O atributo User_Content_Read_Only ou contagem de bytes escritos (Media_Errors_Corrected) pode ser mais relevante do que setores realocados em unidades NVMe.

A automação do monitoramento de disco transforma a administração de infraestrutura de reativa para proativa. Ao implementar este script Python com smartctl, você garante visibilidade total sobre a integridade física do seu armazenamento, permitindo planos de substituição ordenados e evitando surpresas desagradáveis em produção.

Para dúvidas ou ajustes específicos de configuração para seu ambiente, consulte a documentação oficial do smartmontools e adapte os thresholds conforme a tolerância ao risco da sua organização.

Compartilhar: Link copiado!
Esse tutorial foi útil?

Comentários (0)

Seja o primeiro a comentar.

Deixe seu comentário

Seu comentário será analisado antes de ser publicado.

0/2000