Önceki bölümlerimizde karar verebilen, döngülerle binlerce veriyi işleyebilen ve fonksiyonlarla kendi kendini tekrar etmeyen modüler betikler yazmayı öğrendik. Ancak yazdığımız kod ne kadar mükemmel olursa olsun, gerçek dünya (özellikle sistem ve ağ altyapıları) kaotiktir.
Bağlanmaya çalıştığınız switch'in fişi çekilmiş olabilir, okumak istediğiniz log dosyası yanlışlıkla silinmiş olabilir veya bir API size beklediğinizden farklı formatta bir veri gönderebilir. Bu tarz durumlarda betiğimizin çirkin kırmızı hata mesajlarıyla aniden çökmesi (crash) yerine; hatayı öngörmesi, kayıt altına alması (loglaması) ve çalışmaya devam etmesi gerekir. Bu bölümde, "Kırılmaz" (Robust) kod yazmanın iki altın anahtarına odaklanıyoruz: Hata Yakalama ve Dosya İşlemleri.
Neden Hata Yakalamaya İhtiyacımız Var?
Varsayalım ki ağınızdaki 100 farklı yönlendiriciden (router) veri çeken bir döngü yazdınız. Eğer 42. yönlendirici yanıt vermezse ve siz kodunuzda bir hata yakalama mekanizması kurmadıysanız, program 42. adımda "TimeoutError" verip tamamen duracaktır. Geriye kalan 58 cihaza hiçbir işlem yapılamayacaktır. Kurumsal otomasyonlarda bu durum kabul edilemez.
Güvenlik Ağı: Try - Except Blokları
Python'da riskli gördüğümüz, hata fırlatma potansiyeli olan kodları try (dene) bloğunun içine yazarız. Eğer bu blokta işler ters giderse, program çökmek yerine hemen except (hata durumunda) bloğuna atlar ve bizim belirlediğimiz acil durum planını uygular.
Sistemci Senaryosu: Loglardan gelen metin tabanlı bir port numarasını tam sayıya (integer) çevirirken yanlış bir veri gelmesi durumu.
okunan_deger = "BilinmeyenPort"
try:
# Riskli işlem: Metni sayıya çevirmeye çalış
port_no = int(okunan_deger)
print(f"Hedef porta bağlanılıyor: {port_no}")
except ValueError:
# Hata olursa burası çalışır ve program çökmez
print(f"HATA: Geçersiz bir port formatı okundu ({okunan_deger}). Atlanıyor...")
print("Programın geri kalanı çalışmaya devam ediyor...")Tüm Hataları Yakalamak vs. Spesifik Hatalar
Sistem otomasyonunda karşımıza birçok farklı hata çıkabilir (FileNotFoundError, ConnectionError, ZeroDivisionError vb.). En iyi pratik (best practice), ne tür bir hata beklediğinizi bilmek ve sadece onu yakalamaktır. Ancak bazen öngöremediğimiz her şeyi yakalamak için genel bir şemsiye kullanırız.
Bunun için hatayı Exception as e şeklinde bir değişkene atayarak, hatanın teknik detayını da görebiliriz.
hedef_ip = "192.168.1.10"
try:
# Burada cihazın bağlanamadığını simüle edelim (Gerçek bir kütüphane yerine hata fırlatıyoruz)
# raise Exception("Cihaz yanıt vermiyor: Timeout")
10 / 0 # Matematiksel bir hata (ZeroDivisionError)
except ZeroDivisionError:
print("Sistemik Hata: Bir sayı sıfıra bölünemez!")
except Exception as e:
# Beklenmeyen diğer tüm hatalar buraya düşer
print(f"[{hedef_ip}] Cihazında beklenmeyen bir sorun oluştu: Detay: {e}")Finally Bloğu: Her Durumda Çalışması Gerekenler
Bir cihaza SSH ile bağlandığınızı ve konfigürasyon çekerken hata aldığınızı düşünün. Hata olsa da olmasa da, açtığınız o oturumu veya bağlantıyı kapatmanız gerekir; aksi takdirde cihazın VTY hatlarını (portlarını) kilitlersiniz. finally bloğu, hata olsa da olmasa da kesinlikle çalışan bir veda bloğudur.
try:
print("1. Cihaz ile SSH oturumu açıldı.")
# Burada bir hata oluştuğunu varsayalım
print(1 / 0)
print("2. Konfigürasyon çekildi.") # Üstte hata olduğu için burası hiç okunmaz
except Exception as hata:
print(f"İşlem sırasında hata oluştu: {hata}")
finally:
# Yukarıda ne yaşanırsa yaşansın burası çalışır.
print("3. Kapanış: SSH oturumu güvenli bir şekilde sonlandırıldı.")Sistemin Belleği: Dosya İşlemleri (I/O)
Yaptığımız otomasyonların kalıcı olabilmesi için sonuçları bir metin belgesine kaydetmemiz (.txt, .log, .csv) veya mevcut konfigürasyon dosyalarını okumamız gerekir.
Python'da dosya işlemlerini open() fonksiyonuyla yaparız. Dosyaları hangi modda açacağımızı belirten harfler vardır:
'r' (Read - Okuma): Sadece okumak için açar. (Varsayılandır)
'w' (Write - Yazma): Dosyaya yazar. Dosya yoksa oluşturur, varsa içindekileri tamamen silip üstüne yazar!
'a' (Append - Ekleme): Dosya varsa içindekileri silmez, yeni yazılanları dosyanın en sonuna ekler (Log tutmak için idealdir).
Güvenli Dosya Yönetimi: "with open" Yapısı
Dosyalarla çalışırken en büyük risk, işiniz bittiğinde dosyayı kapatmayı (dosya.close()) unutmaktır. Kapatılmayan dosyalar sistem tarafından kilitli kalır ve RAM tüketir. Python, bunu bizim yerimize otomatik yapan muazzam bir yapı sunar: Context Manager (with open). Bu bloktan çıktığınız an dosya kendiliğinden kapanır.
Dosyadan Veri Okumak (Read):
IP adreslerinin bulunduğu bir sunucular.txt dosyasını okuyalım.
# 'with' bloğu kullanıldığı için .close() yazmamıza gerek kalmaz.
try:
with open("sunucular.txt", "r") as dosya:
icerik = dosya.read()
print("Okunan IP Listesi:\n", icerik)
except FileNotFoundError:
print("HATA: 'sunucular.txt' isimli dosya sistemde bulunamadı!")Log Dosyasına Veri Eklemek (Append):
Otomasyonumuzun sonuçlarını bir log dosyasına yazdıralım.
import datetime
suan = datetime.datetime.now()
log_mesaji = f"[{suan}] - Yedekleme islemi basariyla tamamlandi.\n"
with open("otomasyon_kayitlari.log", "a") as log_dosyasi:
log_dosyasi.write(log_mesaji)
print("Log dosyasına yeni kayıt eklendi.")Artık dış dünyadan gelen hatalara karşı hazırlıklı ve verileri kalıcı dosyalara yazıp okuyabilen profesyonel bir altyapımız var. Hata yönetimi (Try-Except) ve dosya okuma/yazma (I/O) yetenekleri, bir script'i "öğrenci projesinden" çıkarıp "üretim (production) ortamı koduna" dönüştüren en kritik adımlardı.
