From 6909041aedd674e55cd27aeb62bb52ba1846e92d Mon Sep 17 00:00:00 2001 From: Wartana Date: Thu, 26 Feb 2026 21:41:36 +0800 Subject: [PATCH] Refactor sync_routing.py: download file dari scheduler MikroTik --- bgp_export_script.rsc | 27 +++++++++++ sync_routing.py | 101 ++++++++++++++++++++++++------------------ 2 files changed, 84 insertions(+), 44 deletions(-) create mode 100644 bgp_export_script.rsc diff --git a/bgp_export_script.rsc b/bgp_export_script.rsc new file mode 100644 index 0000000..f9457a3 --- /dev/null +++ b/bgp_export_script.rsc @@ -0,0 +1,27 @@ +/system/script remove [find name="bgp_lokal_export"] + +/system/script add name=bgp_lokal_export dont-require-permissions=yes source={ \ +:local fname "bgp_lokal_export"; \ +:log info "BGP Export: mulai..."; \ +:local output ""; \ +:local cnt 0; \ +:foreach r in=[/routing/route find where distance=15] do={ \ + :local dst [/routing/route get \$r dst-address]; \ + :if (\$dst != "0.0.0.0/0" && \$dst != "::/0") do={ \ + :set output ("\$output\$dst\n"); \ + :set cnt (\$cnt + 1); \ + }; \ +}; \ +:log info "BGP Export: CDN selesai"; \ +:foreach r in=[/routing/route find where distance=200] do={ \ + :local dst [/routing/route get \$r dst-address]; \ + :if (\$dst != "0.0.0.0/0" && \$dst != "::/0") do={ \ + :set output ("\$output\$dst\n"); \ + :set cnt (\$cnt + 1); \ + }; \ +}; \ +/file print file=\$fname; \ +:delay 2s; \ +/file set "\$fname.txt" contents=\$output; \ +:log info "BGP Export: selesai"; \ +} diff --git a/sync_routing.py b/sync_routing.py index af182ca..1193638 100644 --- a/sync_routing.py +++ b/sync_routing.py @@ -18,57 +18,70 @@ API_URL = f"http://{BGP_ROUTER_IP}/rest/routing/route" auth = (BGP_ROUTER_USER, BGP_ROUTER_PASSWORD) def get_local_bgp_routes(): - """Mengambil rentang IP Lokal/CDN dari tabel routing BGP secara efisien menggunakan parameter query API MikroTik""" + """Download file bgp_lokal_export.txt dari router BGP. + + File ini diekspor secara berkala oleh scheduler MikroTik (bgp_lokal_scheduler) + yang menjalankan script bgp_lokal_export setiap hari jam 04:00. + File berisi daftar dst-address dari rute CDN (distance=15) dan NIX (distance=200). + """ + import urllib3 + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + print(f"Menghubungi router {BGP_ROUTER_IP} via REST API...") - all_local_ips = [] + session = requests.Session() + session.auth = auth + session.verify = False + api_url = f"http://{BGP_ROUTER_IP}/rest" + export_file = "bgp_lokal_export.txt" + + # 1. Cek apakah file export ada di router + print(f"Mengecek file {export_file} di router...") try: - # PENGAMBILAN RUTE KONTEN / CDN - # Di RouterOS v7, rute CDN diset distance-nya menjadi -5 dari base eBGP (20), sehingga menjadi 15. - # Kita menggunakan query string "?distance=15&.proplist=dst-address" agar routeros - # hanya me-return rute yang bersangkutan secara instan tanpa perlu meloop 900.000 rute. - print("Mengambil rute Konten / CDN (Distance 15)... Ini dapat memakan waktu hingga satu menit.") - res_cdn = requests.get( - API_URL, - auth=auth, - params={"distance": "15", ".proplist": "dst-address"}, - verify=False, - timeout=120 - ) - if res_cdn.status_code == 200: - cdn_routes = res_cdn.json() - ids = [r.get("dst-address") for r in cdn_routes if r.get("dst-address")] - print(f" -> Berhasil mengambil {len(ids)} rute CDN.") - all_local_ips.extend(ids) - else: - print(f" -> Error CDN: {res_cdn.status_code} - {res_cdn.text}") + res = session.get(f"{api_url}/file", params={"name": export_file}, timeout=10) + if res.status_code == 200: + files = res.json() + if not files: + print(f" -> File {export_file} tidak ditemukan di router!") + print(" Pastikan script bgp_lokal_export sudah dibuat dan dijalankan di router.") + print(" Lihat instruksi di setup_scheduler.md") + return [] - # PENGAMBILAN RUTE NIX / LOKAL OpenIXP - # Berdasarkan konfigurasi bgp-router.rsc, peer NIX menggunakan local.role=ibgp (Distance base: 200). - print("Mengambil rute NIX / OpenIXP (Distance 200)... Ini dapat memakan waktu hingga satu menit.") - res_nix = requests.get( - API_URL, - auth=auth, - params={"distance": "200", ".proplist": "dst-address"}, - verify=False, - timeout=120 - ) - if res_nix.status_code == 200: - nix_routes = res_nix.json() - ids = [r.get("dst-address") for r in nix_routes if r.get("dst-address")] - print(f" -> Berhasil mengambil {len(ids)} rute NIX.") - all_local_ips.extend(ids) + size = files[0].get("size", "0") + print(f" -> File ditemukan ({size} bytes)") else: - print(f" -> Error NIX: {res_nix.status_code} - {res_nix.text}") - - # Hapus default routes (0.0.0.0/0 dan ::/0) jika ada - all_local_ips = [ip for ip in all_local_ips if ip not in ("0.0.0.0/0", "::/0")] + print(f" -> Error cek file: {res.status_code}") + return [] + except Exception as e: + print(f" -> Gagal koneksi ke router: {e}") + return [] + + # 2. Download isi file via /execute :put + print(f"Mengunduh isi {export_file}...") + try: + res = session.post(f"{api_url}/execute", + json={"script": f':put [/file get {export_file} contents]'}, + timeout=30) - return list(set(all_local_ips)) # Kembalikan list unik - - except requests.exceptions.RequestException as e: - print(f"Gagal mengambil data dari router via API: {e}") + if res.status_code == 200: + content = res.json().get("ret", "") + if content: + # Parse: setiap baris adalah dst-address (contoh: 103.10.0.0/16) + routes = [line.strip() for line in content.split("\n") + if line.strip() and "/" in line.strip()] + # Hapus default routes + routes = [r for r in routes if r not in ("0.0.0.0/0", "::/0")] + print(f" -> Berhasil: {len(routes)} rute lokal diunduh") + return list(set(routes)) + else: + print(" -> File kosong!") + return [] + else: + print(f" -> Error download: {res.status_code} - {res.text[:100]}") + return [] + except Exception as e: + print(f" -> Exception: {e}") return [] def generate_address_list_script(route_list, list_name, filename):