163 lines
5.8 KiB
Python
163 lines
5.8 KiB
Python
import os
|
|
import requests
|
|
import json
|
|
import traceback
|
|
import paramiko
|
|
from scp import SCPClient
|
|
|
|
def get_routers_from_config(config_path, isp_name="dimensi"):
|
|
"""Mengambil list router dari config.json billing-mcp"""
|
|
try:
|
|
with open(config_path, 'r') as f:
|
|
config = json.load(f)
|
|
|
|
isp_data = config.get("isps", {}).get(isp_name)
|
|
if not isp_data:
|
|
print(f"ISP {isp_name} tidak ditemukan dalam config.")
|
|
return []
|
|
|
|
routers = isp_data.get("routers", {})
|
|
router_list = []
|
|
for name, data in routers.items():
|
|
if name.startswith(f"router-{isp_name}"):
|
|
router_list.append({
|
|
"name": name,
|
|
"host": data.get("host"),
|
|
"port": data.get("port"),
|
|
"user": data.get("user"),
|
|
"pass": data.get("pass")
|
|
})
|
|
return router_list
|
|
except Exception as e:
|
|
print(f"Error membaca config.json: {e}")
|
|
return []
|
|
|
|
def deploy_via_rest_api(router, rsc_filepath="routing-lokal.rsc"):
|
|
"""Mengirim rute IP satu per satu via REST API Session"""
|
|
host = router["host"]
|
|
user = router["user"]
|
|
password = router["pass"]
|
|
name = router["name"]
|
|
port = router["port"]
|
|
|
|
api_url = f"http://{host}/rest"
|
|
if port != 80:
|
|
api_url = f"http://{host}:{port}/rest"
|
|
|
|
print(f"\n--- Memproses {name} [{host}:{port}] via API ---")
|
|
if not os.path.exists(rsc_filepath):
|
|
print(f"Error: {rsc_filepath} tidak ditemukan!")
|
|
return
|
|
|
|
# Parsing list name dan IPs dari file rsc
|
|
ips = []
|
|
list_name = "ip-lokal" # Default
|
|
with open(rsc_filepath, 'r') as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if line.startswith("remove [find"):
|
|
import re
|
|
match = re.search(r'list="([^"]+)"', line)
|
|
if match:
|
|
list_name = match.group(1)
|
|
elif line.startswith("add list="):
|
|
import re
|
|
match = re.search(r'address="([^"]+)"', line)
|
|
if match:
|
|
ips.append(match.group(1))
|
|
|
|
if not ips:
|
|
print("Tidak ada IP yang ditemukan di dalam file RSC.")
|
|
return
|
|
|
|
print(f"Ditemukan {len(ips)} subnet untuk dimasukkan pada list '{list_name}'.")
|
|
|
|
session = requests.Session()
|
|
session.auth = (user, password)
|
|
session.verify = False
|
|
|
|
# 1. Menghapus list lama di router (loop sampai benar-benar bersih)
|
|
print(f"Menghapus address-list '{list_name}' lama di router...")
|
|
del_payload = {"script": f"/ip/firewall/address-list/remove [find list=\"{list_name}\"]"}
|
|
|
|
for attempt in range(100):
|
|
try:
|
|
session.post(f"{api_url}/execute", json=del_payload, timeout=60)
|
|
except Exception as e:
|
|
print(f" Warning execute remove: {e}")
|
|
|
|
import time
|
|
time.sleep(5)
|
|
|
|
# Verifikasi: cek apakah sudah benar-benar kosong
|
|
try:
|
|
res_check = session.get(f"{api_url}/ip/firewall/address-list",
|
|
params={"list": list_name, ".proplist": ".id"}, timeout=10)
|
|
remaining = len(res_check.json()) if res_check.status_code == 200 else -1
|
|
except:
|
|
remaining = -1
|
|
|
|
if remaining == 0:
|
|
print(f" Address-list '{list_name}' bersih (percobaan {attempt + 1})")
|
|
break
|
|
elif remaining > 0:
|
|
print(f" Sisa {remaining} entry, menghapus ulang... (round {attempt + 1})")
|
|
else:
|
|
print(f" Gagal verifikasi, lanjut...")
|
|
break
|
|
else:
|
|
print(f" Warning: address-list belum bersih setelah 100 percobaan")
|
|
|
|
import time
|
|
time.sleep(1)
|
|
|
|
# 2. Add IP lewat eksekusi Batch Script (Mencegah Payload Too Large)
|
|
print(f"Mengirim {len(ips)} address ke router secara batch... (±500 list per request)")
|
|
|
|
success_count = 0
|
|
fail_count = 0
|
|
|
|
start_time = time.time()
|
|
|
|
chunk_size = 500
|
|
for i in range(0, len(ips), chunk_size):
|
|
chunk_ips = ips[i:i + chunk_size]
|
|
# Buat kumpulan string baris perintah MikroTik dibungkus error handler
|
|
# agar jika ada 1 IP invalid, tidak menghentikan keseluruhan 500 IP lainnya (abort script)
|
|
commands = [f"do {{ /ip/firewall/address-list/add list={list_name} address={ip} }} on-error={{}}" for ip in chunk_ips]
|
|
script_code = "\n".join(commands)
|
|
|
|
payload = {
|
|
"script": script_code
|
|
}
|
|
|
|
try:
|
|
# Karena execution berjalan di background, kita bisa beri jeda aman antar batch
|
|
res = session.post(f"{api_url}/execute", json=payload, timeout=30)
|
|
if res.status_code in (200, 201):
|
|
success_count += len(chunk_ips)
|
|
else:
|
|
fail_count += len(chunk_ips)
|
|
print(f"Error pada batch {i}-{i+chunk_size}: {res.text}")
|
|
time.sleep(0.5) # Hindari CPU router Spike 100%
|
|
except Exception as e:
|
|
fail_count += len(chunk_ips)
|
|
print(f"Exception batch {i}-{i+chunk_size}: {e}")
|
|
|
|
print(f"Progress batch terkirim: {min(i+chunk_size, len(ips))}/{len(ips)}...")
|
|
|
|
elapsed = time.time() - start_time
|
|
print(f"Selesai! {name} terupdate dalam {elapsed:.1f} dtk.")
|
|
print(f"Berhasil: {success_count} IPs, Gagal: {fail_count} IPs.")
|
|
|
|
if __name__ == "__main__":
|
|
config_path = "/home/wartana/myApp/billing-mcp/config.json"
|
|
routers = get_routers_from_config(config_path, "dimensi")
|
|
|
|
if routers:
|
|
print(f"Ditemukan {len(routers)} router distribusi dimensi.")
|
|
for r in routers:
|
|
deploy_via_rest_api(r, "routing-lokal.rsc")
|
|
else:
|
|
print("Tidak ada router target di config.")
|