\x89PNG\r\n\x1a\n\x00\x00\x00\x0DIHDR\x00\x00\x00\x01\x00 \x00\x00\x01\x08\x06\x00\x00\x00\x1F\x15\xC4\x89\x00\x00\x00 \x0AIDATx\x9Ccb\x00\x00\x00\x06\x00\x03\x1A\x05\x9D\x00\x00 \x00\x00IEND\xAE\x42\x60\x82
| Path : /var/www/html/yedekledb24/DatabaseBackupMaster/ |
|
B-Con CMD Config cPanel C-Rdp D-Log Info Jump Mass Ransom Symlink vHost Zone-H |
| Current File : /var/www/html/yedekledb24/DatabaseBackupMaster/windows_sql_agent.py |
#!/usr/bin/env python3
"""
Windows SQL Server Yedekleme Agent'ı
------------------------------------
Bu script Windows SQL Server üzerinde çalışır ve veritabanı yedeklerini alarak
merkezi yedekleDB24 sunucusuna gönderir.
Kullanım:
- Windows'da servis veya zamanlanmış görev olarak çalıştırılır
- yedekleDB24 API'sinden görevleri çeker
- Yerel SQL Server yedeklerini temp dizinine alır
- Yedeği sıkıştırır
- FTP/SFTP üzerinden merkezi sisteme aktarır
- Sonucu API'ye bildirir
"""
import os
import sys
import time
import shutil
import logging
import requests
import subprocess
import tempfile
import json
import zipfile
import ftplib
import paramiko
import psutil
from datetime import datetime
from pathlib import Path
# Loglama ayarları
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler("sql_agent.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger("windows_sql_agent")
# Agent yapılandırması - bu değerler config.json'dan yüklenebilir
CONFIG = {
"api_url": "http://yedekledb24.example.com/api/agent",
"api_key": "YOUR_API_KEY", # Güvenli bir yöntemle saklanmalı
"agent_id": "WIN-SQL-AGENT-01",
"check_interval": 300, # 5 dakika
"temp_dir": "C:\\temp\\sqlbackups",
"min_free_space_gb": 10, # GB cinsinden minimum boş alan
"retry_count": 3,
"sqlcmd_path": r"C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\SQLCMD.EXE"
}
def check_disk_space(path, min_gb=10):
"""
Belirtilen dizinde yeterli boş alan olup olmadığını kontrol eder.
Args:
path (str): Kontrol edilecek dizin
min_gb (int): Minimum GB cinsinden boş alan
Returns:
bool: Yeterli alan varsa True, yoksa False
"""
# Dizin yoksa oluştur
os.makedirs(path, exist_ok=True)
# Disk alanını kontrol et
free_bytes = psutil.disk_usage(path).free
free_gb = free_bytes / (1024**3) # Byte -> GB
logger.info(f"Disk kontrolü: {path} dizininde {free_gb:.2f} GB boş alan var.")
return free_gb >= min_gb
def perform_sql_backup(db_name, backup_file, instance="localhost"):
"""
SQL Server veritabanının yedeğini alır.
Args:
db_name (str): Veritabanı adı
backup_file (str): Yedek dosyasının tam yolu
instance (str): SQL Server instance adı
Returns:
bool: Başarılı ise True, değilse False
"""
# BACKUP DATABASE komutu için SQL dosyası oluştur
sql_file = os.path.join(tempfile.gettempdir(), f"backup_{db_name}_{int(time.time())}.sql")
backup_command = f"""
BACKUP DATABASE [{db_name}]
TO DISK = N'{backup_file}'
WITH NOFORMAT, NOINIT,
NAME = N'{db_name}_backup',
SKIP, NOREWIND, NOUNLOAD, STATS = 10
"""
with open(sql_file, 'w') as f:
f.write(backup_command)
# SQLCMD ile yedeği al
cmd = [
CONFIG["sqlcmd_path"],
'-S', instance,
'-E', # Windows Authentication
'-i', sql_file,
'-b' # Hata durumunda çıkış
]
try:
logger.info(f"SQL Backup başlatılıyor: {db_name}")
process = subprocess.run(cmd, capture_output=True, text=True)
# SQL dosyasını temizle
if os.path.exists(sql_file):
os.remove(sql_file)
# Başarı kontrolü
if process.returncode != 0:
logger.error(f"SQL Backup hatası: {process.stderr}")
return False
logger.info(f"SQL Backup başarılı: {db_name} -> {backup_file}")
return True
except Exception as e:
logger.error(f"SQL Backup işleminde hata: {str(e)}")
# SQL dosyasını temizlemeyi dene
if os.path.exists(sql_file):
os.remove(sql_file)
return False
def compress_file(input_file, output_file=None):
"""
Dosyayı ZIP formatında sıkıştırır
Args:
input_file (str): Sıkıştırılacak dosya
output_file (str, optional): Çıktı dosyası (None ise input + .zip)
Returns:
str: Sıkıştırılmış dosya yolu veya None
"""
if not output_file:
output_file = input_file + '.zip'
try:
logger.info(f"Dosya sıkıştırılıyor: {input_file}")
original_size = os.path.getsize(input_file)
with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as zipf:
zipf.write(input_file, arcname=os.path.basename(input_file))
compressed_size = os.path.getsize(output_file)
compression_ratio = ((original_size - compressed_size) / original_size) * 100
logger.info(f"Sıkıştırma tamamlandı. Oran: {compression_ratio:.2f}% kazanç")
logger.info(f"Orijinal: {original_size/1024/1024:.2f} MB, "
f"Sıkıştırılmış: {compressed_size/1024/1024:.2f} MB")
return output_file
except Exception as e:
logger.error(f"Dosya sıkıştırma hatası: {str(e)}")
return None
def upload_to_ftp(local_file, remote_path, ftp_config):
"""
Dosyayı FTP sunucusuna yükler
Args:
local_file (str): Yerel dosya yolu
remote_path (str): Uzak FTP yolu
ftp_config (dict): FTP yapılandırması
Returns:
bool: Başarılı ise True, değilse False
"""
try:
logger.info(f"FTP yükleme başlatılıyor: {local_file}")
ftp = ftplib.FTP()
ftp.connect(ftp_config['host'], ftp_config.get('port', 21))
ftp.login(ftp_config['username'], ftp_config['password'])
# Uzak dizin kontrolü
remote_dir = os.path.dirname(remote_path)
try:
# Dizini oluştur (gerekirse)
for folder in remote_dir.split('/'):
if folder and folder != '.':
try:
ftp.mkd(folder)
except:
# Dizin zaten var
pass
ftp.cwd(folder)
except:
pass
# Ana dizine dön
ftp.cwd('/')
# Dosyayı yükle
with open(local_file, 'rb') as file:
ftp.storbinary(f'STOR {remote_path}', file)
ftp.quit()
logger.info(f"FTP yükleme başarılı: {remote_path}")
return True
except Exception as e:
logger.error(f"FTP yükleme hatası: {str(e)}")
return False
def upload_to_sftp(local_file, remote_path, sftp_config):
"""
Dosyayı SFTP sunucusuna yükler
Args:
local_file (str): Yerel dosya yolu
remote_path (str): Uzak SFTP yolu
sftp_config (dict): SFTP yapılandırması
Returns:
bool: Başarılı ise True, değilse False
"""
try:
logger.info(f"SFTP yükleme başlatılıyor: {local_file}")
transport = paramiko.Transport((sftp_config['host'], sftp_config.get('port', 22)))
transport.connect(username=sftp_config['username'], password=sftp_config['password'])
sftp = paramiko.SFTPClient.from_transport(transport)
# Uzak dizin kontrolü
remote_dir = os.path.dirname(remote_path)
try:
# Dizini oluştur (gerekirse)
current_dir = ''
for folder in remote_dir.split('/'):
if folder and folder != '.':
current_dir += '/' + folder
try:
sftp.stat(current_dir)
except:
sftp.mkdir(current_dir)
except:
pass
# Dosyayı yükle
sftp.put(local_file, remote_path)
sftp.close()
transport.close()
logger.info(f"SFTP yükleme başarılı: {remote_path}")
return True
except Exception as e:
logger.error(f"SFTP yükleme hatası: {str(e)}")
return False
def get_pending_backup_jobs():
"""
yedekleDB24 API'sinden bekleyen yedekleme görevlerini alır
Returns:
list: Backup job listesi
"""
try:
url = f"{CONFIG['api_url']}/pending-jobs"
headers = {
'X-API-Key': CONFIG['api_key'],
'X-Agent-ID': CONFIG['agent_id']
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.json()['jobs']
else:
logger.error(f"API hatası: {response.status_code} - {response.text}")
return []
except Exception as e:
logger.error(f"API bağlantı hatası: {str(e)}")
return []
def update_job_status(job_id, status, message, backup_file=None, backup_size=None):
"""
Görev durumunu yedekleDB24 API'sine bildirir
Args:
job_id (int): Görev ID
status (str): Durum (success, failed)
message (str): Durum mesajı
backup_file (str, optional): Yedek dosya yolu
backup_size (int, optional): Yedek boyutu (byte)
Returns:
bool: Başarılı ise True, değilse False
"""
try:
url = f"{CONFIG['api_url']}/update-job/{job_id}"
headers = {
'X-API-Key': CONFIG['api_key'],
'X-Agent-ID': CONFIG['agent_id'],
'Content-Type': 'application/json'
}
data = {
'status': status,
'message': message,
'completed_at': datetime.now().isoformat()
}
if backup_file:
data['backup_file'] = backup_file
if backup_size:
data['backup_size'] = backup_size
response = requests.post(url, headers=headers, json=data)
if response.status_code == 200:
return True
else:
logger.error(f"Durum güncelleme hatası: {response.status_code} - {response.text}")
return False
except Exception as e:
logger.error(f"API güncelleme hatası: {str(e)}")
return False
def process_backup_job(job):
"""
Yedekleme görevini işler
Args:
job (dict): Görev bilgileri
Returns:
bool: Başarılı ise True, değilse False
"""
job_id = job['id']
db_name = job['database']
instance = job.get('instance', 'localhost')
logger.info(f"Görev {job_id} işleniyor: {db_name} veritabanı yedekleniyor")
# Temp dizini kontrolü
temp_dir = CONFIG['temp_dir']
os.makedirs(temp_dir, exist_ok=True)
# Disk alanı kontrolü
if not check_disk_space(temp_dir, CONFIG['min_free_space_gb']):
error_msg = f"Yetersiz disk alanı: {temp_dir} dizininde en az {CONFIG['min_free_space_gb']} GB boş alan gerekli"
logger.error(error_msg)
update_job_status(job_id, "failed", error_msg)
return False
# Yedek dosya yolunu oluştur
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_file = os.path.join(temp_dir, f"{db_name}_{timestamp}.bak")
# SQL Yedekleme
if not perform_sql_backup(db_name, backup_file, instance):
update_job_status(job_id, "failed", "SQL Server yedekleme hatası")
return False
# Dosya doğrulama
if not os.path.exists(backup_file) or os.path.getsize(backup_file) == 0:
update_job_status(job_id, "failed", "Yedek dosyası oluşturulamadı veya boş")
return False
backup_size = os.path.getsize(backup_file)
logger.info(f"Yedek alındı: {backup_file} ({backup_size/1024/1024:.2f} MB)")
# Dosya sıkıştırma
if job.get('compress', True):
compressed_file = compress_file(backup_file)
if compressed_file:
original_file = backup_file
backup_file = compressed_file
backup_size = os.path.getsize(backup_file)
# Orijinal dosyayı sil
try:
os.remove(original_file)
except:
pass
# Uzak sunucuya yükleme
storage = job.get('storage', {})
storage_type = storage.get('type', 'ftp')
remote_path = storage.get('path', '')
# Uzak yolu oluştur
remote_filename = os.path.basename(backup_file)
if remote_path:
if not remote_path.endswith('/'):
remote_path += '/'
remote_full_path = remote_path + remote_filename
else:
remote_full_path = remote_filename
# Yükleme
upload_success = False
retry_count = CONFIG['retry_count']
while retry_count > 0 and not upload_success:
if storage_type == 'ftp':
upload_success = upload_to_ftp(backup_file, remote_full_path, storage)
elif storage_type == 'sftp':
upload_success = upload_to_sftp(backup_file, remote_full_path, storage)
else:
logger.error(f"Desteklenmeyen depolama türü: {storage_type}")
break
retry_count -= 1
if not upload_success and retry_count > 0:
logger.warning(f"Yükleme başarısız, {retry_count} deneme kaldı")
time.sleep(10) # 10 saniye bekle
# Durum güncelleme
if upload_success:
update_job_status(
job_id,
"success",
f"Yedekleme başarılı: {remote_full_path}",
backup_file=remote_full_path,
backup_size=backup_size
)
# Yerel dosyayı temizle
try:
os.remove(backup_file)
except:
pass
return True
else:
update_job_status(job_id, "failed", "Yedek dosyası yüklenemedi")
return False
def main():
"""
Ana fonksiyon
"""
logger.info(f"Windows SQL Server Yedekleme Agent'ı başlatıldı. Agent ID: {CONFIG['agent_id']}")
# Ana döngü
while True:
try:
# Bekleyen görevleri al
jobs = get_pending_backup_jobs()
logger.info(f"{len(jobs)} bekleyen görev bulundu")
# Görevleri işle
for job in jobs:
process_backup_job(job)
# Bekleme
logger.info(f"{CONFIG['check_interval']} saniye bekleniyor...")
time.sleep(CONFIG['check_interval'])
except KeyboardInterrupt:
logger.info("Agent durduruldu.")
break
except Exception as e:
logger.error(f"Beklenmeyen hata: {str(e)}")
time.sleep(60) # Hata durumunda 1 dakika bekle
if __name__ == "__main__":
main()