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/configgibi izinli bir dosya kullan.chmod 600ile sadece root okuyabilsin. - Log tut. Rapor oluşturma ve gönderim loglarını
/var/log/sysreport.loggibi bir dosyaya at. Bir şeyler ters gittiğinde nerede sorun olduğunu anlarsın. - Hata toleransı ekle.
psutilbazen 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 ortamlardadatetime.utcnow()veyatimezone awaredatetime kullan. - virtualenv’i cron’da doğru kullan. Cron job’da Python yolunu tam belirt:
/opt/sysreport/venv/bin/python, sadecepython3yazma. - 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.