Переписан BGP сервер на FRR
This commit is contained in:
+108
-53
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Скрипт для загрузки префиксов из текстовых файлов в ExaBGP
|
||||
Скрипт для загрузки префиксов из текстовых файлов в FRR
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import subprocess
|
||||
import ipaddress
|
||||
from pathlib import Path
|
||||
from typing import List, Set
|
||||
@@ -21,7 +21,7 @@ def log_message(message: str):
|
||||
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_entry = f"[{timestamp}] {message}"
|
||||
|
||||
# Вывод в stdout для ExaBGP
|
||||
# Вывод в stdout
|
||||
print(log_entry, flush=True)
|
||||
|
||||
# Запись в файл (с обработкой ошибок)
|
||||
@@ -84,44 +84,84 @@ def load_all_prefixes() -> Set[str]:
|
||||
|
||||
return all_prefixes
|
||||
|
||||
def run_vtysh_command(command: str) -> bool:
|
||||
"""Выполнение команды через vtysh"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['vtysh', '-c', command],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
log_error(f"vtysh command failed: {command}")
|
||||
log_error(f"stderr: {result.stderr}")
|
||||
return False
|
||||
|
||||
return True
|
||||
except subprocess.TimeoutExpired:
|
||||
log_error(f"vtysh command timeout: {command}")
|
||||
return False
|
||||
except Exception as e:
|
||||
log_error(f"Error running vtysh command '{command}': {e}")
|
||||
return False
|
||||
|
||||
def announce_prefixes(prefixes: Set[str]):
|
||||
"""Анонсирование префиксов через ExaBGP API"""
|
||||
# Получение настроек из переменных окружения
|
||||
neighbor_ip = os.environ.get('BGP_NEIGHBOR_IP', '192.168.1.1')
|
||||
local_as = int(os.environ.get('BGP_LOCAL_AS', '65000'))
|
||||
router_id = os.environ.get('BGP_ROUTER_ID', '192.168.1.100')
|
||||
"""Анонсирование префиксов через FRR"""
|
||||
log_message(f"Announcing {len(prefixes)} prefixes...")
|
||||
|
||||
for prefix in sorted(prefixes):
|
||||
# Формат команды для ExaBGP (упрощенный)
|
||||
command = {
|
||||
"command": "announce route",
|
||||
"neighbor": neighbor_ip,
|
||||
"attribute": {
|
||||
"origin": "igp",
|
||||
"as-path": [local_as],
|
||||
"next-hop": router_id
|
||||
},
|
||||
"nlri": prefix
|
||||
}
|
||||
|
||||
# Отправка команды в ExaBGP
|
||||
print(json.dumps(command), flush=True)
|
||||
log_message(f"Announced prefix: {prefix}")
|
||||
# Добавление статического маршрута в FRR
|
||||
command = f"ip route {prefix} Null0"
|
||||
if run_vtysh_command(command):
|
||||
log_message(f"Announced prefix: {prefix}")
|
||||
else:
|
||||
log_error(f"Failed to announce prefix: {prefix}")
|
||||
|
||||
def withdraw_prefixes(prefixes: Set[str]):
|
||||
"""Отзыв префиксов через ExaBGP API"""
|
||||
# Получение настроек из переменных окружения
|
||||
neighbor_ip = os.environ.get('BGP_NEIGHBOR_IP', '192.168.1.1')
|
||||
"""Отзыв префиксов через FRR"""
|
||||
log_message(f"Withdrawing {len(prefixes)} prefixes...")
|
||||
|
||||
for prefix in sorted(prefixes):
|
||||
command = {
|
||||
"command": "withdraw route",
|
||||
"neighbor": neighbor_ip,
|
||||
"nlri": prefix
|
||||
}
|
||||
# Удаление статического маршрута из FRR
|
||||
command = f"no ip route {prefix} Null0"
|
||||
if run_vtysh_command(command):
|
||||
log_message(f"Withdrew prefix: {prefix}")
|
||||
else:
|
||||
log_error(f"Failed to withdraw prefix: {prefix}")
|
||||
|
||||
def check_frr_status() -> bool:
|
||||
"""Проверка статуса FRR"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['vtysh', '-c', 'show version'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
return result.returncode == 0
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def wait_for_frr():
|
||||
"""Ожидание запуска FRR"""
|
||||
log_message("Waiting for FRR to start...")
|
||||
max_attempts = 30
|
||||
attempt = 0
|
||||
|
||||
while attempt < max_attempts:
|
||||
if check_frr_status():
|
||||
log_message("FRR is ready")
|
||||
return True
|
||||
|
||||
print(json.dumps(command), flush=True)
|
||||
log_message(f"Withdrew prefix: {prefix}")
|
||||
attempt += 1
|
||||
time.sleep(2)
|
||||
if attempt % 5 == 0:
|
||||
log_message(f"Still waiting for FRR... (attempt {attempt}/{max_attempts})")
|
||||
|
||||
log_error("FRR did not start within expected time")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Основная функция"""
|
||||
@@ -138,8 +178,21 @@ def main():
|
||||
print(f"Found {len(prefixes)} prefixes: {list(prefixes)}")
|
||||
return
|
||||
|
||||
# Проверка режима отзыва
|
||||
if len(sys.argv) > 1 and sys.argv[1] == '--withdraw':
|
||||
log_message("Withdraw mode: loading and withdrawing all prefixes...")
|
||||
prefixes = load_all_prefixes()
|
||||
if wait_for_frr():
|
||||
withdraw_prefixes(prefixes)
|
||||
return
|
||||
|
||||
log_message("Starting prefix loader...")
|
||||
|
||||
# Ожидание запуска FRR
|
||||
if not wait_for_frr():
|
||||
log_error("Cannot proceed without FRR")
|
||||
return
|
||||
|
||||
# Загрузка префиксов
|
||||
prefixes = load_all_prefixes()
|
||||
log_message(f"Total prefixes loaded: {len(prefixes)}")
|
||||
@@ -152,29 +205,31 @@ def main():
|
||||
announce_prefixes(prefixes)
|
||||
log_message("All prefixes announced successfully")
|
||||
|
||||
# Ожидание команд от ExaBGP
|
||||
# Мониторинг изменений в файлах
|
||||
log_message("Starting file monitoring...")
|
||||
|
||||
try:
|
||||
while True:
|
||||
line = sys.stdin.readline()
|
||||
if not line:
|
||||
break
|
||||
time.sleep(30) # Проверка каждые 30 секунд
|
||||
|
||||
line = line.strip()
|
||||
if not line: # Пропуск пустых строк
|
||||
continue
|
||||
|
||||
# Обработка команд от ExaBGP
|
||||
try:
|
||||
data = json.loads(line)
|
||||
log_message(f"Received command: {data}")
|
||||
except json.JSONDecodeError:
|
||||
# Не все сообщения от ExaBGP являются JSON
|
||||
# Это может быть обычное текстовое сообщение
|
||||
if line.startswith('[') and line.endswith(']'):
|
||||
# Это может быть лог сообщение от ExaBGP
|
||||
log_message(f"Received log message: {line}")
|
||||
else:
|
||||
log_message(f"Received non-JSON message: {line}")
|
||||
# Перезагрузка префиксов
|
||||
new_prefixes = load_all_prefixes()
|
||||
|
||||
# Определение изменений
|
||||
added = new_prefixes - prefixes
|
||||
removed = prefixes - new_prefixes
|
||||
|
||||
if added:
|
||||
log_message(f"Adding {len(added)} new prefixes: {list(added)}")
|
||||
announce_prefixes(added)
|
||||
|
||||
if removed:
|
||||
log_message(f"Removing {len(removed)} prefixes: {list(removed)}")
|
||||
withdraw_prefixes(removed)
|
||||
|
||||
if added or removed:
|
||||
prefixes = new_prefixes
|
||||
log_message(f"Updated prefix count: {len(prefixes)}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
log_message("Received interrupt signal")
|
||||
|
||||
Reference in New Issue
Block a user