Python ile IPv6 ağ sağlığını izleyen küçük bir araç yazmak
Selamlar arkadaşlar, bu yazımızda Python ile küçük ama gerçekten faydalı bir IPv6 izleme aracı yazacağız. Adres dağıtımı, prefix delegation falan değil derdimiz; konumuz daha sıradan ama bir o kadar can sıkıcı bir mesele: IPv6 tarafı ne durumda, kimse fark etmeden çökmüş olabilir mi? Hadi başlayalım.
İşin aslı şu: çoğu şirkette IPv4 izlemesi yıllardır oturmuş, IPv6 ise ya yarı kurulu durumda ya da ' çalışıyor herhalde' modunda. Tecrübeyle sabittir, IPv6 routing'i bir gece sessizce düşer ve bunu fark eden ilk kişi genellikle bir kullanıcı olur. Bu yüzden ufak bir Python script'i bile büyük fark yaratıyor.
Neden Python ve neden asyncio?
ping6 veya ping -6 zaten her Linux kutusunda var; biz onun üzerine ince bir katman yazıyoruz. Tek bir host için subprocess yeter, ama gerçekçi bir senaryoda 20-30 host paralel kontrol edilmek istenir. Sıralı ping yapsanız her hedef için 2-3 saniye bekler, toplam tur 60 saniyeye dayanır. Asyncio ile bunu birkaç saniyeye indiriyoruz.
Şahsi kanaatim, bu tür araçlarda framework kullanmaya gerek yok; standart kütüphane fazlasıyla yetiyor.
Tek host için ping fonksiyonu
Hadi minimum bir örnekle başlayalım. Aşağıdaki fonksiyon ping6 çıktısını parse edip yapılandırılmış bir sonuç döndürüyor:
import asyncio
import re
from dataclasses import dataclass
@dataclass
class PingSonuc:
host: str
ulasilabilir: bool
rtt_ms: float | None
paket_kaybi_yuzde: float
async def ping6_async(host: str, sayi: int = 3) -> PingSonuc:
proc = await asyncio.create_subprocess_exec(
'ping', '-6', '-c', str(sayi), '-W', '2', host,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.DEVNULL,
)
stdout, _ = await proc.communicate()
cikti = stdout.decode()
kayip = re.search(r'(\d+(?:\.\d+)?)% packet loss', cikti)
paket_kaybi = float(kayip.group(1)) if kayip else 100.0
rtt = re.search(r'min/avg/max.*= [\d.]+/([\d.]+)/', cikti)
rtt_ms = float(rtt.group(1)) if rtt else None
return PingSonuc(host, proc.returncode == 0, rtt_ms, paket_kaybi)
Burada create_subprocess_exec ile ping komutunu non-blocking şekilde çağırıyoruz. -W 2 parametresi her bir paket için en fazla 2 saniye bekleyeceğimizi söylüyor; donmuş hedefler bütün döngüyü tıkamasın diye bu önemli.
Paralel toplu kontrol
Tek host'a bakmak ısınma turu. Şimdi gerçek senaryoya geçelim. Bir hedef listesini paralel pingleyip sonuçları toplayalım:
async def topla(hedefler: list[str]) -> list[PingSonuc]:
gorevler = [ping6_async(h) for h in hedefler]
return await asyncio.gather(*gorevler, return_exceptions=False)
async def main():
hedefler = [
'2001:4860:4860::8888', # Google DNS
'2606:4700:4700::1111', # Cloudflare DNS
'2001:db8:cafe::1', # ic gateway
]
sonuclar = await topla(hedefler)
for s in sonuclar:
durum = 'UP' if s.ulasilabilir else 'DOWN'
rtt = f'{s.rtt_ms:.1f}ms' if s.rtt_ms else 'N/A'
print(f'[{durum}] {s.host} rtt={rtt} kayip={s.paket_kaybi_yuzde:.0f}%')
asyncio.run(main())
20 hedef için bu kod yaklaşık 3 saniyede toplam sonucu veriyor; sıralı versiyonun yanından bile geçmiyor. Tabii burada ICMP rate limit'lere de dikkat etmek lazım, ağ ekibi sizi düşman zannetmesin.
SLO bütçesi: sadece UP/DOWN değil
Bana sorarsanız, gerçek değer UP/DOWN'da değil, gecikme dağılımında saklı. Bir host hâlâ ping'e cevap veriyor olabilir ama RTT 12 ms'den 180 ms'ye fırladıysa kullanıcı zaten yandı. Basit bir SLO sınıfı:
def slo_durumu(s: PingSonuc, esik_ms: float = 50.0) -> str:
if not s.ulasilabilir:
return 'KRITIK'
if s.paket_kaybi_yuzde > 0:
return 'BOZULDU'
if s.rtt_ms and s.rtt_ms > esik_ms:
return 'YAVAS'
return 'SAGLIKLI'
Bu üç-dört durumlu çıktı, alert kuralları yazmayı çok rahatlatıyor. Tek bir gauge yerine bir histogram tutarsanız Grafana'da p95'i takip etmek de kolay olur ama bu yazının kapsamı dışına taşar.
Sık karşılaşılan hatalar
-Wparametresini unutmak: Hedef sessizce paket düşürüyorsa varsayılan timeout dakikalara çıkabilir. Tüm async döngünüz tek bir DOWN host yüzünden kilitlenir.-W 2veya-W 3koymak zorunlu.- Sadece DNS adı denemek: Hostname AAAA kaydı dönmüyorsa Linux sessizce IPv4'e fallback yapabilir; siz IPv6'yı izlediğinizi sanırken aslında IPv4 ölçüyorsunuzdur. Mümkünse hedefi doğrudan IPv6 literali olarak verin,
requestskullanıyorsanız URL'de köşeli parantezle (https://[2001:db8::1]/health). - Tek thread'de senkron
subprocess.run: 30 hedef için döngü kuruyorsanız asyncio'ya geçmek üç katı performans değil, on katı performans demek.asyncio.gatherburada hayat kurtarıyor. - Loopback üzerinde test edip iyi sandığınız kod:
::1her zaman ulaşılabilir; gerçek bir test ortamı için harici bir IPv6 hedefi şart, yoksa script'iniz sahada ilk ICMP filtresinde patlar.
Doğrulama
Local'de bir tail komutuyla manuel doğrulamak en hızlısı:
python ipv6_monitor.py | grep -E 'DOWN|YAVAS'
Beklediğiniz çıktı yoksa, yani her şey UP ve hızlı görünüyorsa, bilerek olmayan bir adres ekleyin (2001:db8:dead::1 gibi). DOWN satırı görüyor musunuz? Görüyorsanız hata yolu çalışıyor demektir.
Kapanış
Bu yazıda Python ve asyncio ile basit ama işine yarayan bir IPv6 sağlık izleme aracı yazdık; UP/DOWN'un ötesine geçip RTT ve paket kaybı eşikleriyle SLO mantığı ekledik. Bence bu küçük script kurumsal izleme yığınınızın yerine geçmez ama IPv6 tarafının gerçekten ne durumda olduğunu öğrenmenin en hızlı yolu hâlâ böyle 80 satırlık bir Python dosyası. Umarım faydalı olur, bir sonraki yazıda görüşmek üzere.
