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