import os import zipfile import boto3 from boto3.s3.transfer import TransferConfig from django.utils.text import slugify from datetime import datetime import requests import stat haric_dosya_uzantilari = ['.zip', ] excluded_folders = ['venv', 'yedek', '.idea', '.sock'] hostname = "ams1.vultrobjects.com" secret_key = "Ec1pq3OQAObFLOQrfAVqJKhDAk4BkT7OqgYszlef" access_key = "KQAOMJ8CQ8HP4CY23YPK" x = 1 def upload_file_via_presigned_url(url, file_path): if not os.path.exists(file_path): print(f"Dosya bulunamadi: {file_path}") return False with open(file_path, 'rb') as file_data: try: response = requests.put(url, data=file_data) if response.status_code == 200: print("Dosya yuklendi!") return True else: print(f"Yukleme olmadi. Status code: {response.status_code}") print(f"Response: {response.content}") return False except Exception as e: print(f"Yukleme hatasi: {e}") return False def get_filtered_folder_names(directory, excluded_folders): folder_names = [] for item in os.listdir(directory): item_path = os.path.join(directory, item) if os.path.isdir(item_path) and item not in excluded_folders: folder_names.append(item) return folder_names def zip_klasor(ziplenecek_klasor, hedef_zip_adi, haric_klasorler=[], haric_dosya_uzantilari=[]): # Parametrelerin geçerliliğini kontrol et if not ziplenecek_klasor or not hedef_zip_adi: raise ValueError("Ziplenecek klasör ve hedef zip adı boş olamaz") if not os.path.exists(ziplenecek_klasor): raise FileNotFoundError(f"Ziplenecek klasör bulunamadı: {ziplenecek_klasor}") # Hedef zip dosyasının bulunacağı dizini oluştur ve izinleri ayarla hedef_dizin = os.path.dirname(hedef_zip_adi) # Eğer hedef dizin boşsa, mevcut dizini kullan if not hedef_dizin: hedef_dizin = "." hedef_zip_adi = os.path.join(hedef_dizin, hedef_zip_adi) if not os.path.exists(hedef_dizin): os.makedirs(hedef_dizin, mode=0o755, exist_ok=True) # Zip dosyası oluşturmadan önce izinleri kontrol et if os.path.exists(hedef_zip_adi): try: os.chmod(hedef_zip_adi, 0o666) except Exception as e: print(f"Mevcut zip dosyasinin izinleri guncellenemedi: {e}") with zipfile.ZipFile(hedef_zip_adi, 'w', zipfile.ZIP_DEFLATED) as zipf: for klasor_yolu, _, dosya_listesi in os.walk(ziplenecek_klasor): if not any(k in klasor_yolu for k in haric_klasorler): for dosya in dosya_listesi: dosya_adi, dosya_uzantisi = os.path.splitext(dosya) dosya_yolu = os.path.join(klasor_yolu, dosya) # Dosyanın var olup olmadığını kontrol et if not os.path.exists(dosya_yolu): print(f"Dosya bulunamadi: {dosya_yolu}") continue # Socket dosyalarını atla try: file_stat = os.stat(dosya_yolu) if stat.S_ISSOCK(file_stat.st_mode): print(f"Socket dosyasi atlandi: {dosya_yolu}") continue except (OSError, PermissionError) as e: print(f"Dosya stat alinamadi: {dosya_yolu} -> Hata: {e}") continue if dosya_uzantisi.lower() not in haric_dosya_uzantilari: try: # Dosya okuma izinlerini kontrol et if os.access(dosya_yolu, os.R_OK): zipf.write(dosya_yolu, os.path.relpath(dosya_yolu, ziplenecek_klasor)) print(f"Dosya eklendi: {dosya_yolu}") else: print(f"Dosya okuma izni yok: {dosya_yolu}") except (PermissionError, OSError) as e: print(f"Dosya eklenemedi: {dosya_yolu} -> Hata: {e}") except Exception as e: print(f"Beklenmeyen hata: {dosya_yolu} -> Hata: {e}") # Oluşturulan zip dosyasının izinlerini ayarla try: os.chmod(hedef_zip_adi, 0o644) print(f"Zip dosyasi olusturuldu: {hedef_zip_adi}") except Exception as e: print(f"Zip dosyasi izinleri ayarlanamadi: {e}") def create_ssh_zip(ssh_manager, source_dir, zip_name, excluded_folders=[], excluded_extensions=[]): """SSH üzerinden uzak sunucuda zip dosyası oluşturur""" # Uzak sunucuda geçici zip dosyası yolu remote_zip_path = f"/tmp/{zip_name}" # Önce kaynak dizinin varlığını kontrol et check_dir_command = f"test -d '{source_dir}' && echo 'exists' || echo 'not_exists'" try: stdout, stderr, status = ssh_manager.execute_command(check_dir_command) if not status or stdout.strip() != "exists": raise Exception(f"Kaynak dizin bulunamadı: {source_dir}") except Exception as e: raise Exception(f"Dizin kontrolü hatası: {str(e)}") # Zip komutunun varlığını kontrol et ve gerekirse kur zip_check_command = "which zip || command -v zip" try: stdout, stderr, status = ssh_manager.execute_command(zip_check_command) if not status: print("Zip komutu bulunamadı, kurulum deneniyor...") if not install_zip_on_remote(ssh_manager): raise Exception("Zip komutu uzak sunucuda bulunamadı ve kurulum başarısız oldu.") except Exception as e: raise Exception(f"Zip komutu kontrolü hatası: {str(e)}") # Hariç tutulacak klasörler için exclude parametresi exclude_args = "" for folder in excluded_folders: exclude_args += f" --exclude='{folder}/*' --exclude='{folder}'" for ext in excluded_extensions: exclude_args += f" --exclude='*{ext}'" # Eski zip dosyasını temizle cleanup_command = f"rm -f '{remote_zip_path}'" ssh_manager.execute_command(cleanup_command) # Zip komutunu oluştur (daha basit ve güvenilir) zip_command = f"cd '{source_dir}' && zip -r '{remote_zip_path}' . {exclude_args}" print(f"Çalıştırılan komut: {zip_command}") try: stdout, stderr, status = ssh_manager.execute_command(zip_command) print(f"Zip komutu sonucu - Status: {status}, Stdout: {stdout}, Stderr: {stderr}") # Zip komutu bazen uyarılarla birlikte başarılı olabilir # Bu yüzden sadece status kontrolü yerine dosya varlığını da kontrol edelim # Zip dosyasının varlığını kontrol et check_command = f"test -f '{remote_zip_path}' && echo 'exists' || echo 'not_exists'" stdout_check, stderr_check, status_check = ssh_manager.execute_command(check_command) if not status_check or stdout_check.strip() != "exists": error_details = f"Status: {status}, Stdout: {stdout}, Stderr: {stderr}" raise Exception(f"Zip dosyası oluşturulamadı. Detaylar: {error_details}") # Dosya boyutunu al size_command = f"stat -c%s '{remote_zip_path}' 2>/dev/null || stat -f%z '{remote_zip_path}' 2>/dev/null || wc -c < '{remote_zip_path}'" stdout_size, stderr_size, status_size = ssh_manager.execute_command(size_command) file_size = 0 if status_size and stdout_size.strip().isdigit(): file_size = int(stdout_size.strip()) else: # Boyut alınamazsa alternatif yöntem ls_command = f"ls -la '{remote_zip_path}'" stdout_ls, stderr_ls, status_ls = ssh_manager.execute_command(ls_command) if status_ls: print(f"Zip dosyası bilgileri: {stdout_ls}") print(f"Zip dosyası başarıyla oluşturuldu: {remote_zip_path}, Boyut: {file_size}") return remote_zip_path, file_size except Exception as e: # Hata durumunda oluşmuş olabilecek zip dosyasını temizle cleanup_command = f"rm -f '{remote_zip_path}'" ssh_manager.execute_command(cleanup_command) raise e def download_ssh_file(ssh_manager, remote_path, local_path): """SSH üzerinden dosya indirir""" try: print(f"Dosya indiriliyor: {remote_path} -> {local_path}") # Local dizinin varlığını kontrol et ve oluştur local_dir = os.path.dirname(local_path) if not os.path.exists(local_dir): os.makedirs(local_dir, mode=0o755, exist_ok=True) # SFTP kullanarak dosyayı indir with ssh_manager.client.open_sftp() as sftp: # Uzak dosyanın varlığını kontrol et try: file_stat = sftp.stat(remote_path) print(f"Uzak dosya boyutu: {file_stat.st_size} byte") except FileNotFoundError: raise Exception(f"Uzak dosya bulunamadı: {remote_path}") sftp.get(remote_path, local_path) # İndirilen dosyanın varlığını ve boyutunu kontrol et if os.path.exists(local_path): local_size = os.path.getsize(local_path) print(f"Dosya başarıyla indirildi. Local boyut: {local_size} byte") return True else: raise Exception("Dosya indirildikten sonra bulunamadı") except Exception as e: print(f"Dosya indirme hatası: {e}") # Başarısız indirme durumunda local dosyayı temizle if os.path.exists(local_path): try: os.remove(local_path) except: pass return False def cleanup_ssh_file(ssh_manager, remote_path): """SSH sunucusunda geçici dosyayı temizler""" try: cleanup_command = f"rm -f '{remote_path}'" ssh_manager.execute_command(cleanup_command) except Exception as e: print(f"Temizleme hatası: {e}") from ssh_manager.models import SSHLog, Project, SSHCredential def job(folder, calisma_dizini, project_id=None): import ssl logs = [] # Parametrelerin geçerliliğini kontrol et if not folder or folder.strip() == "": return {'success': False, 'message': 'Klasör adı boş olamaz', 'logs': logs} if not calisma_dizini or calisma_dizini.strip() == "": return {'success': False, 'message': 'Çalışma dizini boş olamaz', 'logs': logs} if not project_id: return {'success': False, 'message': 'Proje ID gerekli', 'logs': logs} # NOT: calisma_dizini SSH sunucusundaki bir yol olduğu için burada local kontrol yapılmaz # Dizin kontrolü views.py'da SSH üzerinden yapılmalı try: project = Project.objects.get(id=project_id) ssh_manager = project.ssh_credential.get_manager() except Exception as e: return {'success': False, 'message': f'SSH bağlantısı kurulamadı: {str(e)}', 'logs': logs} # --- Vultr/S3 config --- config = { 'access_key': "KQAOMJ8CQ8HP4CY23YPK", 'secret_key': "Ec1pq3OQAObFLOQrfAVqJKhDAk4BkT7OqgYszlef", 'host_base': "ams1.vultrobjects.com", 'bucket_location': "US", 'use_https': True, 'check_ssl_certificate': False, # SSL doğrulamasını kapat 'multipart_chunk_size_mb': 50, # Chunk boyutunu artır } endpoint_url = f"https://{config['host_base']}" region_name = config['bucket_location'] # --- session = boto3.session.Session() client = session.client('s3', region_name=region_name, endpoint_url=endpoint_url, aws_access_key_id=config['access_key'], aws_secret_access_key=config['secret_key'], use_ssl=config['use_https'], verify=False, # SSL doğrulamasını tamamen kapat config=boto3.session.Config( signature_version='s3v4', retries={'max_attempts': 3}, s3={ 'addressing_style': 'path', 'payload_signing_enabled': False, 'chunked_encoding': False } ) ) def log_and_db(msg, status=True): logs.append(msg) if project_id: try: project = Project.objects.get(id=project_id) SSHLog.objects.create( ssh_credential=project.ssh_credential, log_type='backup', command=f'Backup: {folder}', output=msg, status=status ) except Exception: pass log_and_db("S3 oturumu başlatıldı.") local_dt = datetime.now() current_date = slugify(str(local_dt)) # Zip dosyası için tam yol oluştur zip_dosya_adi = folder + "_" + current_date + ".zip" output_zip = os.path.join("/tmp", zip_dosya_adi) # /tmp dizininde oluştur log_and_db(f"SSH üzerinden zip dosyası oluşturuluyor...") try: # SSH üzerinden uzak sunucuda zip oluştur zip_dosya_adi = folder + "_" + current_date + ".zip" log_and_db(f"Kaynak dizin: {calisma_dizini}") log_and_db(f"Zip dosyası adı: {zip_dosya_adi}") try: remote_zip_path, file_size = create_ssh_zip( ssh_manager, calisma_dizini, zip_dosya_adi, excluded_folders, haric_dosya_uzantilari ) log_and_db(f"Uzak sunucuda zip oluşturuldu: {remote_zip_path} ({file_size} byte)") # Zip dosyasını local'e indir local_zip_path = os.path.join("/tmp", zip_dosya_adi) log_and_db(f"Zip dosyası indiriliyor: {local_zip_path}") if not download_ssh_file(ssh_manager, remote_zip_path, local_zip_path): raise Exception("Zip dosyası indirilemedi") log_and_db(f"Zip dosyası başarıyla indirildi") # Uzak sunucudaki geçici zip dosyasını temizle cleanup_ssh_file(ssh_manager, remote_zip_path) output_zip = local_zip_path except Exception as zip_error: log_and_db(f"Zip oluşturma başarısız: {str(zip_error)}") log_and_db(f"Tar ile yedekleme deneniyor...") # Zip başarısız olursa tar kullan tar_dosya_adi = folder + "_" + current_date + ".tar.gz" remote_tar_path, file_size = create_tar_backup( ssh_manager, calisma_dizini, tar_dosya_adi, excluded_folders, haric_dosya_uzantilari ) log_and_db(f"Uzak sunucuda tar.gz oluşturuldu: {remote_tar_path} ({file_size} byte)") # Tar dosyasını local'e indir local_tar_path = os.path.join("/tmp", tar_dosya_adi) log_and_db(f"Tar dosyası indiriliyor: {local_tar_path}") if not download_ssh_file(ssh_manager, remote_tar_path, local_tar_path): raise Exception("Tar dosyası indirilemedi") log_and_db(f"Tar dosyası başarıyla indirildi") # Uzak sunucudaki geçici tar dosyasını temizle cleanup_ssh_file(ssh_manager, remote_tar_path) output_zip = local_tar_path except Exception as e: error_msg = f"SSH zip oluşturma hatası: {str(e)}" log_and_db(f"{error_msg}", status=False) # SSH bağlantısını kapat try: ssh_manager.close() except: pass return {'success': False, 'message': error_msg, 'logs': logs} log_and_db(f"Zip işlemi tamamlandı: {output_zip}") # --- Zip dosyası oluştu mu ve boş mu kontrolü --- if not os.path.exists(output_zip): log_and_db(f"Zip dosyası oluşmadı: {output_zip}", status=False) return {'success': False, 'message': 'Zip dosyası oluşmadı', 'logs': logs} else: size = os.path.getsize(output_zip) log_and_db(f"Zip dosyası boyutu: {size} byte") if size == 0: log_and_db(f"Zip dosyası BOŞ!", status=False) return {'success': False, 'message': 'Zip dosyası boş', 'logs': logs} bucket_name = folder s3_key = output_zip # Bucket içinde alt klasör olmadan doğrudan zip dosyası try: # Bucket kontrol/oluşturma buckets = client.list_buckets() bucket_exists = any(obj['Name'] == bucket_name for obj in buckets['Buckets']) if not bucket_exists: client.create_bucket(Bucket=bucket_name) log_and_db(f"Bucket oluşturuldu: {bucket_name}") else: log_and_db(f"Bucket mevcut: {bucket_name}") # S3'e yükle (Vultr Object Storage için özel yöntem) log_and_db(f"Dosya S3'e yükleniyor: {s3_key}") # Dosya boyutunu kontrol et file_size = os.path.getsize(output_zip) log_and_db(f"Yüklenecek dosya boyutu: {file_size} bytes") try: # Küçük dosyalar için basit put_object kullan if file_size < 50 * 1024 * 1024: # 50MB'dan küçükse with open(output_zip, 'rb') as file_data: client.put_object( Bucket=bucket_name, Key=s3_key, Body=file_data.read(), ACL='private', ContentType='application/zip', Metadata={ 'uploaded_by': 'ssh_manager', 'upload_date': current_date } ) else: # Büyük dosyalar için multipart upload transfer_config = TransferConfig( multipart_threshold=1024 * 1024 * 50, # 50MB max_concurrency=1, # Tek thread kullan multipart_chunksize=1024 * 1024 * 50, # 50MB chunk use_threads=False ) client.upload_file( output_zip, bucket_name, s3_key, ExtraArgs={ 'ACL': 'private', 'ContentType': 'application/zip', 'Metadata': { 'uploaded_by': 'ssh_manager', 'upload_date': current_date } }, Config=transfer_config ) except Exception as upload_error: # Son çare: presigned URL ile yükleme log_and_db(f"Standart yükleme başarısız, presigned URL deneniyor: {upload_error}") try: presigned_url = client.generate_presigned_url( 'put_object', Params={'Bucket': bucket_name, 'Key': s3_key}, ExpiresIn=3600 ) import requests with open(output_zip, 'rb') as file_data: headers = {'Content-Type': 'application/zip'} response = requests.put(presigned_url, data=file_data, headers=headers) if response.status_code not in [200, 201]: raise Exception(f"Presigned URL yükleme hatası: {response.status_code} - {response.text}") except Exception as presigned_error: raise Exception(f"Tüm yükleme yöntemleri başarısız: {presigned_error}") log_and_db(f"S3'e başarıyla yüklendi: {bucket_name}/{s3_key}") except Exception as e: log_and_db(f"S3 yükleme hatası: {e}", status=False) return {'success': False, 'message': str(e), 'logs': logs} finally: if os.path.exists(output_zip): os.remove(output_zip) log_and_db(f"Geçici zip dosyası silindi: {output_zip}") return {'success': True, 'message': 'Yedekleme tamamlandı', 'logs': logs} def install_zip_on_remote(ssh_manager): """Uzak sunucuya zip kurulumu yapar""" # Önce zip komutunun varlığını kontrol et check_zip = "which zip || command -v zip" stdout, stderr, status = ssh_manager.execute_command(check_zip) if status and stdout.strip(): print(f"Zip komutu zaten kurulu: {stdout.strip()}") return True print("Zip komutu bulunamadı, kurulum yapılıyor...") # İşletim sistemi kontrolü os_check = "cat /etc/os-release 2>/dev/null || uname -a" stdout, stderr, status = ssh_manager.execute_command(os_check) install_commands = [] if "ubuntu" in stdout.lower() or "debian" in stdout.lower(): install_commands = [ "sudo apt-get update -y", "sudo apt-get install -y zip unzip" ] elif "centos" in stdout.lower() or "rhel" in stdout.lower() or "red hat" in stdout.lower(): install_commands = [ "sudo yum install -y zip unzip" ] elif "alpine" in stdout.lower(): install_commands = [ "sudo apk update", "sudo apk add zip unzip" ] else: # Diğer sistemler için genel deneme install_commands = [ "sudo apt-get update -y && sudo apt-get install -y zip unzip", "sudo yum install -y zip unzip", "sudo apk add zip unzip" ] # Kurulum komutlarını dene for cmd in install_commands: print(f"Denenen komut: {cmd}") stdout, stderr, status = ssh_manager.execute_command(cmd) if status: # Kurulum sonrası zip kontrolü stdout_check, stderr_check, status_check = ssh_manager.execute_command("which zip") if status_check and stdout_check.strip(): print(f"Zip başarıyla kuruldu: {stdout_check.strip()}") return True else: print(f"Kurulum hatası: {stderr}") print("Zip kurulumu başarısız") return False def create_tar_backup(ssh_manager, source_dir, tar_name, excluded_folders=[], excluded_extensions=[]): """SSH üzerinden tar kullanarak yedek oluşturur (zip alternatifi)""" # Uzak sunucuda geçici tar dosyası yolu remote_tar_path = f"/tmp/{tar_name}" # Kaynak dizinin varlığını kontrol et check_dir_command = f"test -d '{source_dir}' && echo 'exists' || echo 'not_exists'" stdout, stderr, status = ssh_manager.execute_command(check_dir_command) if not status or stdout.strip() != "exists": raise Exception(f"Kaynak dizin bulunamadı: {source_dir}") # Hariç tutulacak klasörler için exclude parametresi exclude_args = "" for folder in excluded_folders: exclude_args += f" --exclude='{folder}'" for ext in excluded_extensions: exclude_args += f" --exclude='*{ext}'" # Eski tar dosyasını temizle cleanup_command = f"rm -f '{remote_tar_path}'" ssh_manager.execute_command(cleanup_command) # Tar komutunu oluştur (gzip ile sıkıştır) tar_command = f"cd '{source_dir}' && tar -czf '{remote_tar_path}' {exclude_args} . 2>/dev/null" print(f"Çalıştırılan tar komutu: {tar_command}") try: stdout, stderr, status = ssh_manager.execute_command(tar_command) print(f"Tar komutu sonucu - Status: {status}, Stdout: {stdout}, Stderr: {stderr}") # Tar dosyasının varlığını kontrol et check_command = f"test -f '{remote_tar_path}' && echo 'exists' || echo 'not_exists'" stdout_check, stderr_check, status_check = ssh_manager.execute_command(check_command) if not status_check or stdout_check.strip() != "exists": error_details = f"Status: {status}, Stdout: {stdout}, Stderr: {stderr}" raise Exception(f"Tar dosyası oluşturulamadı. Detaylar: {error_details}") # Dosya boyutunu al size_command = f"stat -c%s '{remote_tar_path}' 2>/dev/null || stat -f%z '{remote_tar_path}' 2>/dev/null || wc -c < '{remote_tar_path}'" stdout_size, stderr_size, status_size = ssh_manager.execute_command(size_command) file_size = 0 if status_size and stdout_size.strip().isdigit(): file_size = int(stdout_size.strip()) print(f"Tar dosyası başarıyla oluşturuldu: {remote_tar_path}, Boyut: {file_size}") return remote_tar_path, file_size except Exception as e: # Hata durumunda oluşmuş olabilecek tar dosyasını temizle cleanup_command = f"rm -f '{remote_tar_path}'" ssh_manager.execute_command(cleanup_command) raise e