Python ile Otomatik Sistem Raporu Oluşturma ve PDF Çıktısı Alma

Sistem yöneticiliğinde en sık karşılaştığım durumlardan biri şu: Sabah işe geliyorsun, müdür sana “Sunucuların durumu nasıl, bir rapor alabilir misin?” diye soruyor. Elle hazırlamak saatler alıyor, her seferinde aynı bilgileri kopyala-yapıştır yapıyorsun. İşte tam bu noktada Python devreye giriyor. Birkaç satır kod ile sistem bilgilerini otomatik olarak toplayıp, düzgün bir PDF raporu haline getirebilirsin. Bu yazıda bunu nasıl yapacağını adım adım göstereceğim.

Neden Python ve PDF?

Bash scriptleri sistem bilgisi toplamak için harika, ama çıktıyı düzgün bir formata sokmak istediğinde işler karmaşıklaşıyor. Python’da ise hem sistem API’lerine erişim hem de PDF oluşturma kütüphaneleri mevcut. psutil kütüphanesi ile neredeyse her şeyi okuyabilirsin: CPU, RAM, disk, ağ istatistikleri, çalışan süreçler…

PDF formatı da raporlama için ideal çünkü:

  • Platformdan bağımsız görüntüleniyor
  • Formatlamayı bozmuyor
  • E-posta ile kolayca paylaşılabiliyor
  • Arşivleme için uygun
  • Müdürler Word’den çok PDF seviyor (deneyimlerime göre)

Gerekli Kütüphaneleri Kurmak

Başlamadan önce gerekli paketleri kuralım. Ben bu örneklerde psutil, reportlab ve bazı standart kütüphaneleri kullanacağım.

pip install psutil reportlab matplotlib

Eğer sistem genelinde kurmak istiyorsan veya sanal ortam kullanmak istiyorsan:

python3 -m venv /opt/sysreport/venv
source /opt/sysreport/venv/bin/activate
pip install psutil reportlab matplotlib

ReportLab, Python dünyasında PDF oluşturma için en olgun kütüphanelerden biri. Biraz verbose bir API’si var ama bir kez alıştığında çok güçlü. Alternatif olarak fpdf2 de kullanılabilir, daha basit bir API sunuyor. Ben bu yazıda her ikisini de göstereceğim.

Sistem Bilgilerini Toplamak

Önce sistem bilgilerini toplayan modülümüzü yazalım. Bu kodu system_info.py olarak kaydet:

cat > /opt/sysreport/system_info.py << 'EOF'
import psutil
import platform
import socket
import datetime
import os

def get_system_info():
    """Temel sistem bilgilerini toplar"""
    info = {}
    
    # Genel sistem bilgileri
    info['hostname'] = socket.gethostname()
    info['platform'] = platform.system()
    info['platform_release'] = platform.release()
    info['platform_version'] = platform.version()
    info['architecture'] = platform.machine()
    info['processor'] = platform.processor()
    info['python_version'] = platform.python_version()
    info['boot_time'] = datetime.datetime.fromtimestamp(
        psutil.boot_time()
    ).strftime("%Y-%m-%d %H:%M:%S")
    info['report_time'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    # IP adresi
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        info['ip_address'] = s.getsockname()[0]
        s.close()
    except Exception:
        info['ip_address'] = "Belirlenemedi"
    
    return info

def get_cpu_info():
    """CPU kullanim bilgilerini toplar"""
    cpu = {}
    cpu['physical_cores'] = psutil.cpu_count(logical=False)
    cpu['total_cores'] = psutil.cpu_count(logical=True)
    cpu['max_frequency'] = psutil.cpu_freq().max if psutil.cpu_freq() else 0
    cpu['current_frequency'] = psutil.cpu_freq().current if psutil.cpu_freq() else 0
    cpu['usage_percent'] = psutil.cpu_percent(interval=1)
    cpu['per_core_usage'] = psutil.cpu_percent(interval=1, percpu=True)
    
    # Load average (Linux/Mac)
    try:
        cpu['load_avg'] = os.getloadavg()
    except AttributeError:
        cpu['load_avg'] = (0, 0, 0)
    
    return cpu

def get_memory_info():
    """RAM ve swap bilgilerini toplar"""
    memory = {}
    
    ram = psutil.virtual_memory()
    memory['ram_total'] = round(ram.total / (1024**3), 2)
    memory['ram_available'] = round(ram.available / (1024**3), 2)
    memory['ram_used'] = round(ram.used / (1024**3), 2)
    memory['ram_percent'] = ram.percent
    
    swap = psutil.swap_memory()
    memory['swap_total'] = round(swap.total / (1024**3), 2)
    memory['swap_used'] = round(swap.used / (1024**3), 2)
    memory['swap_percent'] = swap.percent
    
    return memory

def get_disk_info():
    """Disk kullanim bilgilerini toplar"""
    disks = []
    for partition in psutil.disk_partitions():
        try:
            usage = psutil.disk_usage(partition.mountpoint)
            disk = {
                'device': partition.device,
                'mountpoint': partition.mountpoint,
                'fstype': partition.fstype,
                'total': round(usage.total / (1024**3), 2),
                'used': round(usage.used / (1024**3), 2),
                'free': round(usage.free / (1024**3), 2),
                'percent': usage.percent
            }
            disks.append(disk)
        except PermissionError:
            continue
    return disks

def get_network_info():
    """Ag arayuz bilgilerini toplar"""
    networks = []
    net_io = psutil.net_io_counters(pernic=True)
    
    for interface, addrs in psutil.net_if_addrs().items():
        net = {'interface': interface, 'addresses': []}
        for addr in addrs:
            addr_info = {
                'family': str(addr.family),
                'address': addr.address
            }
            net['addresses'].append(addr_info)
        
        if interface in net_io:
            io = net_io[interface]
            net['bytes_sent'] = round(io.bytes_sent / (1024**2), 2)
            net['bytes_recv'] = round(io.bytes_recv / (1024**2), 2)
        
        networks.append(net)
    
    return networks

def get_top_processes(n=10):
    """En cok kaynak kullanan processleri listeler"""
    processes = []
    for proc in psutil.process_iter(['pid', 'name', 'cpu_percent', 
                                      'memory_percent', 'status']):
        try:
            processes.append(proc.info)
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            pass
    
    # CPU kullanımına göre sırala
    processes = sorted(processes, 
                       key=lambda x: x['cpu_percent'] or 0, 
                       reverse=True)
    return processes[:n]

EOF
echo "system_info.py olusturuldu"

ReportLab ile PDF Oluşturmak

Şimdi bu bilgileri alıp PDF’e dönüştüren ana scripti yazalım. ReportLab’in SimpleDocTemplate sınıfını kullanacağız:

cat > /opt/sysreport/report_generator.py << 'EOF'
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch, cm
from reportlab.lib import colors
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, 
                                 Table, TableStyle, HRFlowable)
from reportlab.lib.enums import TA_CENTER, TA_LEFT
import datetime
import system_info

def create_styles():
    """Rapor icin stil tanimlamalari"""
    styles = getSampleStyleSheet()
    
    styles.add(ParagraphStyle(
        name='ReportTitle',
        parent=styles['Title'],
        fontSize=20,
        textColor=colors.HexColor('#2C3E50'),
        spaceAfter=10
    ))
    
    styles.add(ParagraphStyle(
        name='SectionHeader',
        parent=styles['Heading2'],
        fontSize=13,
        textColor=colors.HexColor('#2980B9'),
        spaceBefore=15,
        spaceAfter=8,
        borderPad=4
    ))
    
    styles.add(ParagraphStyle(
        name='InfoText',
        parent=styles['Normal'],
        fontSize=9,
        leading=14,
        textColor=colors.HexColor('#2C3E50')
    ))
    
    styles.add(ParagraphStyle(
        name='WarningText',
        parent=styles['Normal'],
        fontSize=9,
        textColor=colors.HexColor('#E74C3C'),
        leading=14
    ))
    
    return styles

def build_info_table(data, col_widths=None):
    """Genel amacli bilgi tablosu olusturur"""
    if col_widths is None:
        col_widths = [6*cm, 11*cm]
    
    table = Table(data, colWidths=col_widths)
    table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (0, -1), colors.HexColor('#EBF5FB')),
        ('TEXTCOLOR', (0, 0), (0, -1), colors.HexColor('#2C3E50')),
        ('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, -1), 9),
        ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#BDC3C7')),
        ('ROWBACKGROUNDS', (0, 0), (-1, -1), 
         [colors.white, colors.HexColor('#FDFEFE')]),
        ('PADDING', (0, 0), (-1, -1), 6),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
    ]))
    return table

def generate_pdf_report(output_file=None):
    """Ana PDF raporu olusturur"""
    if output_file is None:
        timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        hostname = system_info.get_system_info()['hostname']
        output_file = f"sistem_raporu_{hostname}_{timestamp}.pdf"
    
    doc = SimpleDocTemplate(
        output_file,
        pagesize=A4,
        rightMargin=2*cm,
        leftMargin=2*cm,
        topMargin=2*cm,
        bottomMargin=2*cm
    )
    
    styles = create_styles()
    story = []
    
    # --- BASLIK BOLUMU ---
    sys_data = system_info.get_system_info()
    
    story.append(Paragraph("Sistem Durum Raporu", styles['ReportTitle']))
    story.append(Paragraph(
        f"Sunucu: <b>{sys_data['hostname']}</b> | "
        f"Rapor Tarihi: {sys_data['report_time']}",
        styles['InfoText']
    ))
    story.append(HRFlowable(width="100%", thickness=2, 
                             color=colors.HexColor('#2980B9')))
    story.append(Spacer(1, 0.3*cm))
    
    # --- GENEL SISTEM BILGILERI ---
    story.append(Paragraph("Genel Sistem Bilgileri", styles['SectionHeader']))
    
    sys_table_data = [
        ["Hostname", sys_data['hostname']],
        ["IP Adresi", sys_data['ip_address']],
        ["Platform", f"{sys_data['platform']} {sys_data['platform_release']}"],
        ["Mimari", sys_data['architecture']],
        ["Son Acilis", sys_data['boot_time']],
        ["Rapor Zamani", sys_data['report_time']],
    ]
    story.append(build_info_table(sys_table_data))
    story.append(Spacer(1, 0.5*cm))
    
    # --- CPU BILGILERI ---
    story.append(Paragraph("CPU Bilgileri", styles['SectionHeader']))
    cpu_data = system_info.get_cpu_info()
    
    cpu_style = styles['WarningText'] if cpu_data['usage_percent'] > 80 else styles['InfoText']
    
    cpu_table_data = [
        ["Fiziksel Cekirdek", str(cpu_data['physical_cores'])],
        ["Mantiksal Cekirdek", str(cpu_data['total_cores'])],
        ["Mevcut Frekans", f"{cpu_data['current_frequency']:.0f} MHz"],
        ["CPU Kullanimi", f"%{cpu_data['usage_percent']}"],
        ["Load Average (1/5/15 dk)", 
         f"{cpu_data['load_avg'][0]:.2f} / {cpu_data['load_avg'][1]:.2f} / {cpu_data['load_avg'][2]:.2f}"],
    ]
    story.append(build_info_table(cpu_table_data))
    
    if cpu_data['usage_percent'] > 80:
        story.append(Spacer(1, 0.2*cm))
        story.append(Paragraph(
            "UYARI: CPU kullanimi %80'in uzerinde!", 
            styles['WarningText']
        ))
    
    story.append(Spacer(1, 0.5*cm))
    
    # --- BELLEK BILGILERI ---
    story.append(Paragraph("Bellek Kullanimi", styles['SectionHeader']))
    mem_data = system_info.get_memory_info()
    
    mem_table_data = [
        ["Toplam RAM", f"{mem_data['ram_total']} GB"],
        ["Kullanilan RAM", f"{mem_data['ram_used']} GB (%{mem_data['ram_percent']})"],
        ["Bos RAM", f"{mem_data['ram_available']} GB"],
        ["Toplam Swap", f"{mem_data['swap_total']} GB"],
        ["Kullanilan Swap", f"{mem_data['swap_used']} GB (%{mem_data['swap_percent']})"],
    ]
    story.append(build_info_table(mem_table_data))
    
    if mem_data['ram_percent'] > 85:
        story.append(Spacer(1, 0.2*cm))
        story.append(Paragraph(
            "UYARI: RAM kullanimi kritik seviyede!", 
            styles['WarningText']
        ))
    
    story.append(Spacer(1, 0.5*cm))
    
    # --- DISK BILGILERI ---
    story.append(Paragraph("Disk Kullanimi", styles['SectionHeader']))
    disk_data = system_info.get_disk_info()
    
    disk_header = [["Cihaz", "Baglama Noktasi", "Toplam", "Kullanilan", "Bos", "Doluluk"]]
    disk_rows = []
    for disk in disk_data:
        row = [
            disk['device'],
            disk['mountpoint'],
            f"{disk['total']} GB",
            f"{disk['used']} GB",
            f"{disk['free']} GB",
            f"%{disk['percent']}"
        ]
        disk_rows.append(row)
    
    disk_table = Table(
        disk_header + disk_rows,
        colWidths=[3.5*cm, 3.5*cm, 2.5*cm, 2.5*cm, 2.5*cm, 2.5*cm]
    )
    disk_table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#2980B9')),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, -1), 8),
        ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#BDC3C7')),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), 
         [colors.white, colors.HexColor('#FDFEFE')]),
        ('PADDING', (0, 0), (-1, -1), 5),
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
    ]))
    story.append(disk_table)
    
    # Disk uyarilari
    for disk in disk_data:
        if disk['percent'] > 85:
            story.append(Spacer(1, 0.2*cm))
            story.append(Paragraph(
                f"UYARI: {disk['mountpoint']} diski %{disk['percent']} dolu!",
                styles['WarningText']
            ))
    
    story.append(Spacer(1, 0.5*cm))
    
    # --- EN COK KAYNAK KULLANAN PROCESSLER ---
    story.append(Paragraph("En Cok Kaynak Kullanan Processler (Top 10)", 
                           styles['SectionHeader']))
    proc_data = system_info.get_top_processes(10)
    
    proc_header = [["PID", "Process Adi", "CPU %", "Bellek %", "Durum"]]
    proc_rows = []
    for proc in proc_data:
        row = [
            str(proc['pid']),
            proc['name'][:30] if proc['name'] else 'N/A',
            f"{proc['cpu_percent']:.1f}",
            f"{proc['memory_percent']:.1f}" if proc['memory_percent'] else '0.0',
            proc['status'] or 'N/A'
        ]
        proc_rows.append(row)
    
    proc_table = Table(
        proc_header + proc_rows,
        colWidths=[2*cm, 7*cm, 2.5*cm, 2.5*cm, 3*cm]
    )
    proc_table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#2C3E50')),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, -1), 8),
        ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#BDC3C7')),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), 
         [colors.white, colors.HexColor('#FDFEFE')]),
        ('PADDING', (0, 0), (-1, -1), 5),
        ('ALIGN', (2, 0), (-1, -1), 'CENTER'),
    ]))
    story.append(proc_table)
    
    # Footer
    story.append(Spacer(1, 1*cm))
    story.append(HRFlowable(width="100%", thickness=1, 
                             color=colors.HexColor('#BDC3C7')))
    story.append(Spacer(1, 0.2*cm))
    story.append(Paragraph(
        f"Bu rapor otomatik olarak olusturulmustur. | {sys_data['report_time']}",
        ParagraphStyle('Footer', parent=styles['InfoText'], 
                      alignment=TA_CENTER, textColor=colors.grey)
    ))
    
    doc.build(story)
    print(f"Rapor olusturuldu: {output_file}")
    return output_file

if __name__ == "__main__":
    generate_pdf_report()
EOF

Grafik Eklemek

Raporunuzu görselleştirmek istiyorsanız, matplotlib ile pasta veya çubuk grafikler ekleyebilirsiniz. İşte basit bir kullanım grafiği örneği:

cat > /opt/sysreport/chart_helper.py << 'EOF'
import matplotlib
matplotlib.use('Agg')  # GUI olmayan ortamlar icin
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import io
from reportlab.platypus import Image

def create_resource_chart(cpu_percent, ram_percent, swap_percent):
    """Kaynak kullanim grafigi olusturur ve ReportLab Image nesnesi doner"""
    
    fig, axes = plt.subplots(1, 3, figsize=(10, 3))
    fig.patch.set_facecolor('#FAFAFA')
    
    resources = [
        ('CPU', cpu_percent, '#2980B9'),
        ('RAM', ram_percent, '#27AE60'),
        ('Swap', swap_percent, '#E67E22')
    ]
    
    for ax, (label, value, color) in zip(axes, resources):
        # Renk uyarisi
        bar_color = '#E74C3C' if value > 85 else color
        
        ax.barh([''], [value], color=bar_color, height=0.5)
        ax.barh([''], [100 - value], left=[value], 
                color='#ECF0F1', height=0.5)
        ax.set_xlim(0, 100)
        ax.set_title(f'{label}n%{value:.1f}', 
                     fontsize=11, fontweight='bold', color='#2C3E50')
        ax.set_xlabel('Kullanim (%)', fontsize=8)
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        ax.tick_params(axis='y', which='both', left=False, labelleft=False)
    
    plt.tight_layout(pad=2.0)
    
    # BytesIO'ya kaydet, dosyaya degil
    buf = io.BytesIO()
    plt.savefig(buf, format='png', dpi=150, bbox_inches='tight',
                facecolor='#FAFAFA')
    buf.seek(0)
    plt.close(fig)
    
    # ReportLab Image nesnesi olarak don
    img = Image(buf, width=17*cm, height=5*cm)
    return img

EOF
echo "chart_helper.py olusturuldu"

Cron ile Otomatik Rapor Gönderimi

Şimdi asıl gücü ortaya çıkaralım. Raporun her sabah otomatik olarak oluşturulup mail ile gönderilmesini ayarlayalım:

cat > /opt/sysreport/send_report.py << 'EOF'
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders
import report_generator
import system_info

# Yapilandirma - bunlari environment variable olarak tut
SMTP_SERVER = os.environ.get('SMTP_SERVER', 'smtp.gmail.com')
SMTP_PORT = int(os.environ.get('SMTP_PORT', '587'))
SMTP_USER = os.environ.get('SMTP_USER', '[email protected]')
SMTP_PASS = os.environ.get('SMTP_PASS', '')
RECIPIENTS = os.environ.get('REPORT_RECIPIENTS', '[email protected]').split(',')

def send_report_email():
    """Raporu olusturur ve mail olarak gonderir"""
    
    # Once raporu olustur
    sys_data = system_info.get_system_info()
    mem_data = system_info.get_memory_info()
    cpu_data = system_info.get_cpu_info()
    disk_data = system_info.get_disk_info()
    
    pdf_file = report_generator.generate_pdf_report()
    
    # Mail gonderme
    msg = MIMEMultipart()
    msg['From'] = SMTP_USER
    msg['To'] = ', '.join(RECIPIENTS)
    
    # Uyari durumunu kontrol et
    has_warning = (
        cpu_data['usage_percent'] > 80 or
        mem_data['ram_percent'] > 85 or
        any(d['percent'] > 85 for d in disk_data)
    )
    
    prefix = "[UYARI] " if has_warning else "[OK] "
    msg['Subject'] = f"{prefix}Sistem Raporu - {sys_data['hostname']} - {sys_data['report_time']}"
    
    # Mail govdesi
    body = f"""
    Merhaba,
    
    {sys_data['hostname']} sunucusu icin otomatik sistem raporu ekte yer almaktadir.
    
    Ozet:
    - CPU Kullanimi: %{cpu_data['usage_percent']}
    - RAM Kullanimi: %{mem_data['ram_percent']} ({mem_data['ram_used']} GB / {mem_data['ram_total']} GB)
    - Swap Kullanimi: %{mem_data['swap_percent']}
    
    Disk Durumu:
    """
    
    for disk in disk_data:
        durum = "KRITIK" if disk['percent'] > 85 else "Normal"
        body += f"    - {disk['mountpoint']}: %{disk['percent']} ({durum})n"
    
    body += f"n    Detayli rapor ekte PDF olarak bulunmaktadir.n"
    
    msg.attach(MIMEText(body, 'plain', 'utf-8'))
    
    # PDF eki
    with open(pdf_file, 'rb') as f:
        part = MIMEBase('application', 'octet-stream')
        part.set_payload(f.read())
        encoders.encode_base64(part)
        part.add_header(
            'Content-Disposition',
            f'attachment; filename="{os.path.basename(pdf_file)}"'
        )
        msg.attach(part)
    
    # Gonder
    try:
        server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
        server.starttls()
        server.login(SMTP_USER, SMTP_PASS)
        server.sendmail(SMTP_USER, RECIPIENTS, msg.as_string())
        server.quit()
        print(f"Rapor gonderildi: {', '.join(RECIPIENTS)}")
    except Exception as e:
        print(f"Mail gonderim hatasi: {e}")
    finally:
        # Gecici PDF dosyasini temizle
        if os.path.exists(pdf_file):
            os.remove(pdf_file)

if __name__ == "__main__":
    send_report_email()
EOF

# Cron job ekle - her sabah 07:00'de
(crontab -l 2>/dev/null; echo "0 7 * * * [email protected] SMTP_PASS=sifre [email protected] /opt/sysreport/venv/bin/python /opt/sysreport/send_report.py >> /var/log/sysreport.log 2>&1") | crontab -

Birden Fazla Sunucuyu İzlemek

Gerçek dünya senaryosunda tek sunucu nadiren yeterli oluyor. Birden fazla sunucu için uzaktan bilgi toplamak istersen SSH üzerinden bunu yapabilirsin:

cat > /opt/sysreport/multi_server_report.py << 'EOF'
import subprocess
import json
import sys
from report_generator import generate_pdf_report

# Izlenecek sunucular listesi
SERVERS = [
    {'host': 'web01.sirket.com', 'user': 'sysadmin', 'key': '/root/.ssh/id_rsa'},
    {'host': 'db01.sirket.com', 'user': 'sysadmin', 'key': '/root/.ssh/id_rsa'},
    {'host': 'app01.sirket.com', 'user': 'sysadmin', 'key': '/root/.ssh/id_rsa'},
]

REMOTE_SCRIPT = """
import psutil, json, platform, socket, os

result = {
    'hostname': socket.gethostname(),
    'cpu': psutil.cpu_percent(interval=1),
    'ram': psutil.virtual_memory().percent,
    'swap': psutil.swap_memory().percent,
    'disks': [
        {
            'mount': p.mountpoint,
            'percent': psutil.disk_usage(p.mountpoint).percent
        }
        for p in psutil.disk_partitions()
        if p.mountpoint != ''
    ]
}
print(json.dumps(result))
"""

def collect_remote_data(server):
    """SSH ile uzak sunucudan veri toplar"""
    cmd = [
        'ssh', '-i', server['key'],
        '-o', 'StrictHostKeyChecking=no',
        '-o', 'ConnectTimeout=10',
        f"{server['user']}@{server['host']}",
        f'python3 -c "{REMOTE_SCRIPT}"'
    ]
    
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        if result.returncode == 0:
            return json.loads(result.stdout.strip())
        else:
            print(f"Hata [{server['host']}]: {result.stderr}")
            return None
    except subprocess.TimeoutExpired:
        print(f"Zaman asimi [{server['host']}]")
        return None
    except Exception as e:
        print(f"Baglanti hatasi [{server['host']}]: {e}")
        return None

def generate_multi_server_report():
    """Tum sunucular icin ozet rapor olusturur"""
    all_data = []
    
    for server in SERVERS:
        print(f"Veri toplaniyor: {server['host']}")
        data = collect_remote_data(server)
        if data:
            all_data.append(data)
        else:
            all_data.append({
                'hostname': server['host'],
                'error': 'Baglanti kurulamadi',
                'cpu': -1, 'ram': -1, 'swap': -1, 'disks': []
            })
    
    return all_data

if __name__ == "__main__":
    servers_data = generate_multi_server_report()
    for s in servers_data:
        if 'error' not in s:
            print(f"{s['hostname']}: CPU:%{s['cpu']} RAM:%{s['ram']}")
        else:
            print(f"{s['hostname']}: {s['error']}")

EOF
echo "multi_server_report.py olusturuldu"

Pratik İpuçları ve Dikkat Edilmesi Gerekenler

Birkaç yıldır bu tür scriptler kullanıyorum ve şunu öğrendim: Script yazmak kolay, sürdürmek zor. Bazı kritik noktalar:

  • Şifreleri script içine yazma. Environment variable veya /etc/sysreport/config gibi izinli bir dosya kullan. chmod 600 ile sadece root okuyabilsin.
  • Log tut. Rapor oluşturma ve gönderim loglarını /var/log/sysreport.log gibi bir dosyaya at. Bir şeyler ters gittiğinde nerede sorun olduğunu anlarsın.
  • Hata toleransı ekle. psutil bazen bazı processlere erişemez, bu normal. Her yerde try-except bloğu kullan.
  • PDF boyutuna dikkat et. Çok fazla süreç listesi veya grafik eklersen PDF şişiyor. 10-15 process yeterli.
  • Zaman dilimi sorunlarına dikkat et. datetime.now() local time kullanır. Sunucuların farklı timezone’da olduğu ortamlarda datetime.utcnow() veya timezone aware datetime kullan.
  • virtualenv’i cron’da doğru kullan. Cron job’da Python yolunu tam belirt: /opt/sysreport/venv/bin/python, sadece python3 yazma.
  • Rapor arşivi oluştur. Eski raporları /opt/sysreport/archive/ altında tut. 90 günden eski olanları otomatik sil:
# Eski raporlari temizle (90 gunden eski)
find /opt/sysreport/archive/ -name "*.pdf" -mtime +90 -delete

Sonuç

Bu yazıda sıfırdan başlayarak sistematik bir şekilde sistem raporu oluşturma altyapısı kurduk. psutil ile sistem verilerini topladık, reportlab ile profesyonel görünümlü PDF raporlar ürettik, matplotlib ile görselleştirme ekledik ve cron ile her şeyi otomatikleştirdik.

Bu sistem benim kendi ortamımda haftalarca süren “rapor hazırlama” yorgunluğunu tamamen ortadan kaldırdı. Her sabah mail kutusunda düzgün bir rapor görmek gerçekten rahatlatıcı. Üstelik bir sunucuda disk dolmaya başladığında veya CPU yüksek gittiğinde otomatik uyarı alıyorsun.

Kodu ihtiyacına göre genişletebilirsin. Servis durumları (systemctl is-active nginx), son login kayıtları, açık portlar, güvenlik güncellemeleri… Bunların hepsini aynı mantıkla PDF raporuna ekleyebilirsin. Temel altyapı hazır, gerisi yaratıcılık.

Yorum yapın