Переписан BGP сервер на FRR

This commit is contained in:
2025-07-11 18:06:07 +07:00
parent 3c5740784b
commit e197dae9f7
14 changed files with 1222 additions and 812 deletions
+64 -54
View File
@@ -1,85 +1,95 @@
#!/usr/bin/env python3
"""
Скрипт для генерации конфигурации ExaBGP из переменных окружения
Скрипт для генерации конфигурации FRR из переменных окружения
"""
import os
import sys
from pathlib import Path
def get_env_var(name: str, default: str) -> str:
def get_env_var(name, default):
"""Получение переменной окружения с значением по умолчанию"""
return os.environ.get(name, default)
def generate_exabgp_config():
"""Генерация конфигурации ExaBGP"""
def generate_frr_config():
"""Генерация конфигурации FRR"""
# Получение переменных окружения
# Получение настроек из переменных окружения
local_as = get_env_var('BGP_LOCAL_AS', '65000')
router_id = get_env_var('BGP_ROUTER_ID', '192.168.1.100')
neighbor_ip = get_env_var('BGP_NEIGHBOR_IP', '192.168.1.1')
neighbor_as = get_env_var('BGP_NEIGHBOR_AS', '65001')
hold_time = get_env_var('BGP_HOLD_TIME', '90')
keepalive = get_env_var('BGP_KEEPALIVE', '30')
md5_password = get_env_var('BGP_MD5_PASSWORD', '')
log_level = get_env_var('BGP_LOG_LEVEL', 'INFO')
log_level = get_env_var('BGP_LOG_LEVEL', 'info')
# Генерация конфигурации
config = f"""# Автоматически сгенерированная конфигурация ExaBGP
process load-prefixes {{
run /app/scripts/load_prefixes.py;
encoder json;
}}
# Конфигурация FRR
config = f"""# FRR Configuration File
# Generated automatically from environment variables
# Глобальные настройки
frr version 8.5.2
frr defaults traditional
hostname bgp-server
log syslog {log_level}
service integrated-vtysh-config
# Настройки BGP
neighbor {neighbor_ip} {{
router-id {router_id};
local-address {router_id};
local-as {local_as};
peer-as {neighbor_as};
# Только исходящие анонсы (не принимаем префиксы)
capability {{
graceful-restart;
}}
# Настройки сессии
hold-time {hold_time};
"""
router bgp {local_as}
bgp router-id {router_id}
bgp log-neighbor-changes
neighbor {neighbor_ip} remote-as {neighbor_as}
neighbor {neighbor_ip} description RouterOS Client
neighbor {neighbor_ip} timers {keepalive} {hold_time}
neighbor {neighbor_ip} capability graceful-restart
neighbor {neighbor_ip} soft-reconfiguration inbound
!
address-family ipv4 unicast
neighbor {neighbor_ip} activate
neighbor {neighbor_ip} route-map OUTBOUND out
neighbor {neighbor_ip} route-map INBOUND in
exit-address-family
!
# Добавление MD5 пароля если указан
if md5_password:
config += f" md5-password \"{md5_password}\";\n"
config += f"""
# Логирование
api {{
processes [load-prefixes];
neighbor-changes;
receive {{
parsed;
update;
}}
send {{
parsed;
update;
}}
}}
}}
# Route maps для фильтрации
route-map OUTBOUND permit 10
description Allow all outbound routes
!
route-map INBOUND deny 10
description Deny all inbound routes (receive-only mode)
!
# Настройки интерфейса
interface eth0
ip address {router_id}/24
!
# Статические маршруты (будут добавлены динамически)
# ip route 192.168.1.0/24 Null0
"""
return config
def main():
"""Основная функция"""
if len(sys.argv) > 1 and sys.argv[1] == '--print':
# Вывод конфигурации в stdout
print(generate_exabgp_config())
else:
# Запись конфигурации в файл
config = generate_exabgp_config()
with open('/app/exabgp.conf', 'w') as f:
try:
# Генерация конфигурации
config = generate_frr_config()
# Запись в файл
config_path = Path('/etc/frr/frr.conf')
config_path.parent.mkdir(parents=True, exist_ok=True)
with open(config_path, 'w') as f:
f.write(config)
print("Configuration generated successfully")
print(f"FRR configuration generated: {config_path}")
print("Configuration content:")
print(config)
except Exception as e:
print(f"Error generating FRR configuration: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
+108 -53
View File
@@ -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")
+174 -73
View File
@@ -1,93 +1,194 @@
#!/usr/bin/env python3
"""
Скрипт мониторинга BGP сервера
Скрипт мониторинга FRR BGP сервера
"""
import sys
import os
import time
import subprocess
import json
import time
import sys
from datetime import datetime
from pathlib import Path
from typing import Dict, Any
def run_command(cmd):
"""Выполнение команды и возврат результата"""
# Настройки
LOG_FILE = "/app/logs/monitor.log"
STATUS_FILE = "/app/logs/status.json"
def log_message(message: str):
"""Запись сообщения в лог"""
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] {message}"
print(log_entry, flush=True)
try:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
return result.returncode == 0, result.stdout, result.stderr
with open(LOG_FILE, "a") as f:
f.write(log_entry + "\n")
except (PermissionError, OSError):
pass
def run_vtysh_command(command: str) -> str:
"""Выполнение команды через vtysh"""
try:
result = subprocess.run(
['vtysh', '-c', command],
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0:
return result.stdout.strip()
else:
log_message(f"vtysh command failed: {command}")
return ""
except Exception as e:
return False, "", str(e)
log_message(f"Error running vtysh command '{command}': {e}")
return ""
def check_container_status():
"""Проверка статуса Docker контейнера"""
success, output, error = run_command("docker ps --filter name=bgp-server --format '{{.Status}}'")
if success and output.strip():
return True, output.strip()
return False, "Container not running"
def get_bgp_summary() -> Dict[str, Any]:
"""Получение сводки BGP"""
summary = run_vtysh_command("show ip bgp summary")
if not summary:
return {"error": "Could not get BGP summary"}
# Парсинг вывода BGP summary
lines = summary.split('\n')
neighbors = []
for line in lines:
if 'BGP neighbor' in line and 'remote AS' in line:
parts = line.split()
if len(parts) >= 8:
neighbor = {
"ip": parts[2].rstrip(','),
"remote_as": parts[7],
"state": parts[-1] if len(parts) > 7 else "Unknown"
}
neighbors.append(neighbor)
return {
"neighbors": neighbors,
"raw_output": summary
}
def check_bgp_session():
"""Проверка BGP сессии"""
success, output, error = run_command("docker exec bgp-server exabgpcli show neighbor")
if success:
return True, output
return False, "Cannot check BGP session"
def get_bgp_routes() -> Dict[str, Any]:
"""Получение BGP маршрутов"""
routes = run_vtysh_command("show ip bgp")
if not routes:
return {"error": "Could not get BGP routes"}
# Подсчет маршрутов
route_count = 0
for line in routes.split('\n'):
if line.strip() and not line.startswith('BGP table') and not line.startswith('*>'):
if any(char.isdigit() for char in line):
route_count += 1
return {
"route_count": route_count,
"raw_output": routes
}
def check_prefixes():
"""Проверка загруженных префиксов"""
def get_frr_status() -> Dict[str, Any]:
"""Получение статуса FRR"""
version = run_vtysh_command("show version")
if not version:
return {"error": "Could not get FRR version"}
return {
"version": version.split('\n')[0] if version else "Unknown",
"raw_output": version
}
def get_system_status() -> Dict[str, Any]:
"""Получение системного статуса"""
try:
with open("/app/logs/prefixes.log", "r") as f:
lines = f.readlines()
return True, f"Found {len(lines)} log entries"
except FileNotFoundError:
return False, "Prefix log not found"
# Проверка процессов FRR
zebra_running = subprocess.run(['pgrep', 'zebra'], capture_output=True).returncode == 0
bgpd_running = subprocess.run(['pgrep', 'bgpd'], capture_output=True).returncode == 0
# Проверка использования памяти
memory_info = {}
try:
with open('/proc/meminfo', 'r') as f:
for line in f:
if 'MemTotal:' in line:
memory_info['total'] = line.split()[1]
elif 'MemAvailable:' in line:
memory_info['available'] = line.split()[1]
except:
pass
return {
"zebra_running": zebra_running,
"bgpd_running": bgpd_running,
"memory": memory_info
}
except Exception as e:
return {"error": f"Could not get system status: {e}"}
def check_network_connectivity():
"""Проверка сетевого подключения"""
success, output, error = run_command("docker exec bgp-server ping -c 1 192.168.1.1")
return success, "Network connectivity OK" if success else "Network connectivity failed"
def save_status(status: Dict[str, Any]):
"""Сохранение статуса в файл"""
try:
with open(STATUS_FILE, 'w') as f:
json.dump(status, f, indent=2)
except Exception as e:
log_message(f"Error saving status: {e}")
def main():
"""Основная функция мониторинга"""
print("=== BGP Server Monitoring ===")
print(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
"""Основная функция"""
log_message("Starting FRR monitor...")
# Проверка контейнера
print("1. Container Status:")
status, details = check_container_status()
print(f" Status: {'✓ Running' if status else '✗ Stopped'}")
print(f" Details: {details}")
print()
# Создание директории для логов
try:
os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True)
os.makedirs(os.path.dirname(STATUS_FILE), exist_ok=True)
except (PermissionError, OSError):
pass
# Проверка сети
print("2. Network Connectivity:")
net_ok, net_details = check_network_connectivity()
print(f" Status: {'✓ OK' if net_ok else '✗ Failed'}")
print(f" Details: {net_details}")
print()
# Проверка BGP сессии
print("3. BGP Session:")
bgp_ok, bgp_details = check_bgp_session()
print(f" Status: {'✓ Active' if bgp_ok else '✗ Inactive'}")
if bgp_ok:
print(f" Details: {bgp_details[:200]}...")
else:
print(f" Details: {bgp_details}")
print()
# Проверка префиксов
print("4. Prefixes:")
prefix_ok, prefix_details = check_prefixes()
print(f" Status: {'✓ Loaded' if prefix_ok else '✗ Not loaded'}")
print(f" Details: {prefix_details}")
print()
# Общий статус
overall_status = status and net_ok and bgp_ok and prefix_ok
print("=== Overall Status ===")
print(f"Status: {'✓ HEALTHY' if overall_status else '✗ UNHEALTHY'}")
return 0 if overall_status else 1
while True:
try:
# Сбор статуса
status = {
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"frr": get_frr_status(),
"bgp_summary": get_bgp_summary(),
"bgp_routes": get_bgp_routes(),
"system": get_system_status()
}
# Сохранение статуса
save_status(status)
# Логирование важной информации
bgp_summary = status["bgp_summary"]
if "neighbors" in bgp_summary:
for neighbor in bgp_summary["neighbors"]:
log_message(f"BGP neighbor {neighbor['ip']} (AS {neighbor['remote_as']}): {neighbor['state']}")
bgp_routes = status["bgp_routes"]
if "route_count" in bgp_routes:
log_message(f"BGP routes: {bgp_routes['route_count']}")
system = status["system"]
if "zebra_running" in system and "bgpd_running" in system:
if not system["zebra_running"] or not system["bgpd_running"]:
log_message("WARNING: FRR daemons not running properly")
# Ожидание перед следующей проверкой
time.sleep(60) # Проверка каждую минуту
except KeyboardInterrupt:
log_message("Monitor stopped by user")
break
except Exception as e:
log_message(f"Error in monitor loop: {e}")
time.sleep(30) # Короткая пауза при ошибке
if __name__ == "__main__":
sys.exit(main())
main()
+177 -75
View File
@@ -1,103 +1,205 @@
#!/usr/bin/env python3
"""
Скрипт для перезагрузки префиксов без перезапуска BGP сервера
Скрипт для перезагрузки префиксов в FRR
"""
import subprocess
import sys
import os
import time
import subprocess
import ipaddress
from pathlib import Path
from typing import Set
def run_command(cmd):
"""Выполнение команды"""
# Настройки
DATA_DIR = "/app/data"
LOG_FILE = "/app/logs/reload.log"
PREFIX_FILES = ["prefixes.txt", "additional_prefixes.txt"]
def log_message(message: str):
"""Запись сообщения в лог"""
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] {message}"
print(log_entry, flush=True)
try:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
return result.returncode == 0, result.stdout, result.stderr
except Exception as e:
return False, "", str(e)
with open(LOG_FILE, "a") as f:
f.write(log_entry + "\n")
except (PermissionError, OSError):
pass
def reload_prefixes():
"""Перезагрузка префиксов"""
print("Reloading prefixes...")
# Остановка процесса загрузки префиксов
print("Stopping prefix loader process...")
success, output, error = run_command("docker exec bgp-server pkill -f load_prefixes.py")
if not success:
print("Warning: Could not stop prefix loader process")
# Ожидание завершения процесса
time.sleep(2)
# Проверка что процесс остановлен
success, output, error = run_command("docker exec bgp-server pgrep -f load_prefixes.py")
if success:
print("Error: Prefix loader process is still running")
return False
print("Prefix loader process stopped successfully")
# Запуск нового процесса загрузки префиксов
print("Starting new prefix loader process...")
success, output, error = run_command("docker exec bgp-server /app/scripts/load_prefixes.py &")
if success:
print("Prefix loader process started successfully")
def validate_prefix(prefix: str) -> bool:
"""Валидация IP префикса"""
try:
ipaddress.ip_network(prefix, strict=False)
return True
else:
print(f"Error starting prefix loader: {error}")
except ValueError:
return False
def check_status():
"""Проверка статуса после перезагрузки"""
print("Checking status...")
time.sleep(5)
def load_prefixes_from_file(file_path: Path) -> Set[str]:
"""Загрузка префиксов из файла"""
prefixes = set()
# Проверка процесса
success, output, error = run_command("docker exec bgp-server pgrep -f load_prefixes.py")
if success:
print("✓ Prefix loader process is running")
else:
print("✗ Prefix loader process is not running")
if not file_path.exists():
log_message(f"File not found: {file_path}")
return prefixes
try:
with open(file_path, 'r') as f:
for line_num, line in enumerate(f, 1):
line = line.strip()
# Пропуск пустых строк и комментариев
if not line or line.startswith('#'):
continue
# Валидация префикса
if validate_prefix(line):
prefixes.add(line)
else:
log_message(f"Invalid prefix in {file_path}:{line_num}: {line}")
except Exception as e:
log_message(f"Error reading {file_path}: {e}")
return prefixes
def load_all_prefixes() -> Set[str]:
"""Загрузка всех префиксов из всех файлов"""
all_prefixes = set()
for filename in PREFIX_FILES:
file_path = Path(DATA_DIR) / filename
prefixes = load_prefixes_from_file(file_path)
all_prefixes.update(prefixes)
log_message(f"Loaded {len(prefixes)} prefixes from {filename}")
return all_prefixes
def get_current_routes() -> Set[str]:
"""Получение текущих статических маршрутов из FRR"""
try:
result = subprocess.run(
['vtysh', '-c', 'show ip route static'],
capture_output=True,
text=True,
timeout=30
)
if result.returncode != 0:
log_message("Could not get current routes from FRR")
return set()
routes = set()
for line in result.stdout.split('\n'):
line = line.strip()
if line and 'via Null0' in line:
# Парсинг строки маршрута
parts = line.split()
if len(parts) >= 2:
route = parts[1]
if validate_prefix(route):
routes.add(route)
return routes
except Exception as e:
log_message(f"Error getting current routes: {e}")
return set()
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_message(f"vtysh command failed: {command}")
return False
return True
except Exception as e:
log_message(f"Error running vtysh command '{command}': {e}")
return False
def announce_prefixes(prefixes: Set[str]):
"""Анонсирование префиксов через FRR"""
log_message(f"Announcing {len(prefixes)} prefixes...")
# Проверка логов
success, output, error = run_command("docker exec bgp-server tail -n 5 /app/logs/prefixes.log")
if success:
print("Recent log entries:")
print(output)
else:
print("No recent log entries found")
for prefix in sorted(prefixes):
command = f"ip route {prefix} Null0"
if run_vtysh_command(command):
log_message(f"Announced prefix: {prefix}")
else:
log_message(f"Failed to announce prefix: {prefix}")
def withdraw_prefixes(prefixes: Set[str]):
"""Отзыв префиксов через FRR"""
log_message(f"Withdrawing {len(prefixes)} prefixes...")
return True
for prefix in sorted(prefixes):
command = f"no ip route {prefix} Null0"
if run_vtysh_command(command):
log_message(f"Withdrew prefix: {prefix}")
else:
log_message(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 main():
"""Основная функция"""
print("=== BGP Prefix Reloader ===")
log_message("Starting prefix reload...")
# Проверка что контейнер запущен
success, output, error = run_command("docker ps --filter name=bgp-server --format '{{.Status}}'")
if not success or not output.strip():
print("Error: BGP server container is not running")
print("Please start the container first: docker-compose up -d")
# Проверка статуса FRR
if not check_frr_status():
log_message("ERROR: FRR is not running")
return 1
print("BGP server container is running")
# Загрузка новых префиксов
new_prefixes = load_all_prefixes()
log_message(f"Loaded {len(new_prefixes)} prefixes from files")
# Перезагрузка префиксов
if reload_prefixes():
print("Prefix reload completed successfully")
# Проверка статуса
if check_status():
print("✓ All checks passed")
return 0
else:
print("✗ Some checks failed")
return 1
# Получение текущих маршрутов
current_routes = get_current_routes()
log_message(f"Found {len(current_routes)} current routes in FRR")
# Определение изменений
to_add = new_prefixes - current_routes
to_remove = current_routes - new_prefixes
log_message(f"Changes: {len(to_add)} to add, {len(to_remove)} to remove")
# Применение изменений
if to_remove:
withdraw_prefixes(to_remove)
if to_add:
announce_prefixes(to_add)
# Финальная проверка
final_routes = get_current_routes()
log_message(f"Final route count: {len(final_routes)}")
if len(final_routes) == len(new_prefixes):
log_message("Reload completed successfully!")
return 0
else:
print("✗ Prefix reload failed")
log_message("WARNING: Route count mismatch after reload")
return 1
if __name__ == "__main__":
+89 -62
View File
@@ -1,96 +1,123 @@
# PowerShell скрипт для запуска BGP сервера
# Скрипт запуска BGP Server (FRR)
param(
[string]$ContainerName = "bgp-server",
[string]$DataPath = "./data",
[string]$LogsPath = "./logs",
[switch]$Build,
[switch]$Logs,
[switch]$Monitor
[switch]$Force
)
Write-Host "=== BGP Server Management ===" -ForegroundColor Green
Write-Host "=== BGP Server (FRR) Startup Script ===" -ForegroundColor Green
# Проверка Docker
Write-Host "Checking Docker..." -ForegroundColor Yellow
try {
docker --version | Out-Null
Write-Host "✓ Docker is available" -ForegroundColor Green
} catch {
Write-Host "✗ Docker is not available. Please install Docker Desktop." -ForegroundColor Red
exit 1
# Проверка существования контейнера
$containerExists = docker ps -a --filter "name=$ContainerName" --format "table {{.Names}}" | Select-String $ContainerName
if ($containerExists) {
Write-Host "Found existing container: $ContainerName" -ForegroundColor Yellow
if (-not $Force) {
$response = Read-Host "Do you want to stop and remove the existing container? (y/N)"
if ($response -ne "y" -and $response -ne "Y") {
Write-Host "Operation cancelled." -ForegroundColor Red
exit 0
}
}
# Остановка и удаление старого контейнера
Write-Host "Stopping container..." -ForegroundColor Yellow
docker stop $ContainerName
Write-Host "Removing container..." -ForegroundColor Yellow
docker rm $ContainerName
}
# Проверка Docker Compose
Write-Host "Checking Docker Compose..." -ForegroundColor Yellow
try {
docker-compose --version | Out-Null
Write-Host "✓ Docker Compose is available" -ForegroundColor Green
} catch {
Write-Host "✗ Docker Compose is not available." -ForegroundColor Red
exit 1
# Создание директорий если не существуют
if (-not (Test-Path $DataPath)) {
Write-Host "Creating data directory: $DataPath" -ForegroundColor Yellow
New-Item -ItemType Directory -Path $DataPath -Force | Out-Null
}
# Создание необходимых директорий
Write-Host "Creating directories..." -ForegroundColor Yellow
if (!(Test-Path "logs")) {
New-Item -ItemType Directory -Path "logs" | Out-Null
Write-Host "✓ Created logs directory" -ForegroundColor Green
if (-not (Test-Path $LogsPath)) {
Write-Host "Creating logs directory: $LogsPath" -ForegroundColor Yellow
New-Item -ItemType Directory -Path $LogsPath -Force | Out-Null
}
if (!(Test-Path "data")) {
New-Item -ItemType Directory -Path "data" | Out-Null
Write-Host "✓ Created data directory" -ForegroundColor Green
# Создание файла с префиксами если не существует
$prefixesFile = Join-Path $DataPath "prefixes.txt"
if (-not (Test-Path $prefixesFile)) {
Write-Host "Creating default prefixes file..." -ForegroundColor Yellow
@"
# Default prefixes file
# Add your prefixes here, one per line
192.168.1.0/24
10.0.0.0/8
172.16.0.0/12
"@ | Out-File -FilePath $prefixesFile -Encoding UTF8
}
# Сборка образа если требуется
if ($Build) {
Write-Host "Building Docker image..." -ForegroundColor Yellow
docker-compose build
docker build -t bgp-server-frr .
if ($LASTEXITCODE -ne 0) {
Write-Host "✗ Build failed" -ForegroundColor Red
Write-Host "Failed to build image. Exiting." -ForegroundColor Red
exit 1
}
Write-Host "✓ Build completed" -ForegroundColor Green
}
# Остановка существующего контейнера
Write-Host "Stopping existing container..." -ForegroundColor Yellow
docker-compose down 2>$null
Write-Host "✓ Existing container stopped" -ForegroundColor Green
# Запуск контейнера
Write-Host "Starting BGP Server container..." -ForegroundColor Yellow
$currentPath = Get-Location
$dataVolume = "${currentPath}\${DataPath}:/app/data:ro"
$logsVolume = "${currentPath}\${LogsPath}:/app/logs"
# Запуск сервера
Write-Host "Starting BGP server..." -ForegroundColor Yellow
docker-compose up -d
docker run -d `
--name $ContainerName `
--restart unless-stopped `
-p 179:179 `
-v $dataVolume `
-v $logsVolume `
--cap-add NET_ADMIN `
--privileged `
bgp-server-frr
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ BGP server started successfully" -ForegroundColor Green
Write-Host "Container started successfully!" -ForegroundColor Green
# Ожидание запуска
Write-Host "Waiting for server to initialize..." -ForegroundColor Yellow
Write-Host "Waiting for container to start..." -ForegroundColor Yellow
Start-Sleep -Seconds 10
# Проверка статуса
Write-Host "Checking server status..." -ForegroundColor Yellow
$status = docker-compose ps
Write-Host $status
$status = docker ps --filter "name=$ContainerName" --format "table {{.Status}}"
Write-Host "Container status: $status" -ForegroundColor Cyan
if ($Logs) {
Write-Host "Showing logs..." -ForegroundColor Yellow
docker-compose logs -f bgp-server
# Проверка FRR статуса
Write-Host "Checking FRR status..." -ForegroundColor Cyan
docker exec $ContainerName vtysh -c "show version" 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Host "FRR is running successfully!" -ForegroundColor Green
# Показ BGP статуса
Write-Host "BGP Summary:" -ForegroundColor Cyan
docker exec $ContainerName vtysh -c "show ip bgp summary" 2>$null
# Показ логов
Write-Host "Recent logs:" -ForegroundColor Cyan
docker logs --tail=10 $ContainerName
Write-Host "`nBGP Server (FRR) is ready!" -ForegroundColor Green
Write-Host "To view logs: docker logs -f $ContainerName" -ForegroundColor Cyan
Write-Host "To check BGP status: docker exec $ContainerName vtysh -c 'show ip bgp summary'" -ForegroundColor Cyan
Write-Host "To access FRR CLI: docker exec -it $ContainerName vtysh" -ForegroundColor Cyan
} else {
Write-Host "FRR failed to start properly. Check logs:" -ForegroundColor Red
docker logs $ContainerName
}
if ($Monitor) {
Write-Host "Starting monitoring..." -ForegroundColor Yellow
docker exec bgp-server python3 /app/scripts/monitor.py
}
} else {
Write-Host "Failed to start BGP server" -ForegroundColor Red
Write-Host "Check logs with: docker-compose logs bgp-server" -ForegroundColor Yellow
Write-Host "Failed to start container. Check the error above." -ForegroundColor Red
exit 1
}
Write-Host "=== Setup Complete ===" -ForegroundColor Green
Write-Host "Useful commands:" -ForegroundColor Cyan
Write-Host " View logs: docker-compose logs -f bgp-server" -ForegroundColor White
Write-Host " Check status: docker-compose ps" -ForegroundColor White
Write-Host " Stop server: docker-compose down" -ForegroundColor White
Write-Host " Monitor: docker exec bgp-server python3 /app/scripts/monitor.py" -ForegroundColor White
}
+104 -154
View File
@@ -1,56 +1,24 @@
#!/bin/bash
# Скрипт запуска BGP сервера
# Скрипт запуска BGP сервера на FRR
set -e
echo "Starting BGP Server..."
echo "Starting BGP Server (FRR)..."
# Создание директорий если не существуют
mkdir -p /app/logs
mkdir -p /app/run
mkdir -p /run/exabgp
mkdir -p /app/data
mkdir -p /var/run/frr
mkdir -p /etc/frr
# Установка правильных прав доступа
chmod 755 /app/logs
chmod 755 /app/run
chmod 755 /run/exabgp
chmod 755 /app/data
# Удаление старых named pipes если они существуют
echo "Cleaning up old named pipes..."
for dir in /run/exabgp /var/run/exabgp /usr/local/run/exabgp /usr/local/var/run/exabgp; do
if [ -d "$dir" ]; then
echo "Cleaning $dir..."
rm -f $dir/exabgp.in $dir/exabgp.out
rmdir $dir 2>/dev/null || true
fi
done
# Создание named pipes для CLI в стандартном месте
echo "Creating named pipes for ExaBGP CLI..."
for dir in /run/exabgp /var/run/exabgp /usr/local/run/exabgp /usr/local/var/run/exabgp; do
mkdir -p $dir
echo "Creating named pipes in $dir..."
# Проверяем, существуют ли уже named pipes
if [ ! -p "$dir/exabgp.in" ]; then
mkfifo $dir/exabgp.in
chmod 666 $dir/exabgp.in
echo "Created $dir/exabgp.in"
else
echo "Named pipe $dir/exabgp.in already exists"
fi
if [ ! -p "$dir/exabgp.out" ]; then
mkfifo $dir/exabgp.out
chmod 666 $dir/exabgp.out
echo "Created $dir/exabgp.out"
else
echo "Named pipe $dir/exabgp.out already exists"
fi
done
chmod 755 /var/run/frr
chmod 755 /etc/frr
# Проверка наличия файлов с префиксами
if [ ! -f "/app/data/prefixes.txt" ]; then
@@ -62,9 +30,9 @@ fi
# Создание лог-файлов с правильными правами
echo "Creating log files with proper permissions..."
touch /app/logs/prefixes.log
touch /app/logs/exabgp.log
touch /app/logs/frr.log
chmod 666 /app/logs/prefixes.log
chmod 666 /app/logs/exabgp.log
chmod 666 /app/logs/frr.log
echo "Prefixes file content:"
cat /app/data/prefixes.txt
@@ -77,7 +45,7 @@ ls -la /app/scripts/
echo "---"
# Генерация конфигурации из переменных окружения
echo "Generating ExaBGP configuration from environment variables..."
echo "Generating FRR configuration from environment variables..."
echo "Environment variables:"
env | grep BGP_ || echo "No BGP_ variables found"
echo "---"
@@ -85,11 +53,20 @@ echo "---"
python3 /app/scripts/generate_config.py
# Проверка конфигурации
echo "Validating ExaBGP configuration..."
echo "Validating FRR configuration..."
echo "Configuration file content:"
cat /app/exabgp.conf
cat /etc/frr/frr.conf
echo "---"
exabgp --validate /app/exabgp.conf
# Проверка синтаксиса конфигурации FRR
if vtysh -c "show running-config" > /dev/null 2>&1; then
echo "FRR configuration validation passed"
else
echo "FRR configuration validation failed"
echo "Full config content:"
cat /etc/frr/frr.conf
exit 1
fi
# Тестовый запуск скрипта загрузки префиксов
echo "Testing prefix loader script..."
@@ -105,135 +82,108 @@ else
echo "Warning: Cannot reach $NEIGHBOR_IP. BGP session may not establish."
fi
# Запуск ExaBGP в фоновом режиме с логированием
echo "Starting ExaBGP..."
LOG_LEVEL=${BGP_LOG_LEVEL:-INFO}
# Запуск FRR демонов
echo "Starting FRR daemons..."
# Дополнительная проверка конфигурационного файла
echo "Final config file check:"
ls -la /app/exabgp.conf
echo "Config file first few lines:"
head -10 /app/exabgp.conf
echo "---"
# Запуск zebra (обязательный демон для FRR)
echo "Starting zebra daemon..."
zebra -d -f /etc/frr/frr.conf -A 127.0.0.1 -z /var/run/frr/zebra.api
sleep 2
# Проверка что ExaBGP может прочитать файл
echo "Testing ExaBGP config validation:"
if exabgp --validate /app/exabgp.conf; then
echo "Configuration validation passed"
else
echo "Configuration validation failed"
echo "Full config content:"
cat /app/exabgp.conf
exit 1
fi
echo "---"
# Запуск bgpd
echo "Starting bgpd daemon..."
bgpd -d -f /etc/frr/frr.conf -A 127.0.0.1 -z /var/run/frr/zebra.api
sleep 2
# Проверка переменных окружения и путей
echo "Environment check:"
echo "PWD: $PWD"
echo "PATH: $PATH"
echo "PYTHONPATH: $PYTHONPATH"
echo "BGP_ variables:"
env | grep BGP_ || echo "No BGP_ variables found"
echo "---"
# Проверка что демоны запустились
echo "Checking if FRR daemons are running..."
ps aux | grep -E "(zebra|bgpd)" | grep -v grep || echo "No FRR daemons found in ps"
# Запуск ExaBGP
echo "Starting ExaBGP with config: /app/exabgp.conf"
# Проверяем что файл существует и читается
if [ ! -f "/app/exabgp.conf" ]; then
echo "ERROR: Configuration file /app/exabgp.conf not found!"
exit 1
fi
if [ ! -r "/app/exabgp.conf" ]; then
echo "ERROR: Configuration file /app/exabgp.conf is not readable!"
exit 1
fi
# Запуск ExaBGP с правильными параметрами
cd /app
echo "Starting ExaBGP in background..."
# Запуск ExaBGP в фоновом режиме
if [ -n "$LOG_LEVEL" ]; then
nohup exabgp --log.level $LOG_LEVEL /app/exabgp.conf > /app/logs/exabgp.log 2>&1 &
else
nohup exabgp /app/exabgp.conf > /app/logs/exabgp.log 2>&1 &
fi
EXABGP_PID=$!
# Проверяем что процесс запустился
sleep 5
echo "Checking if ExaBGP process is running..."
ps aux | grep exabgp | grep -v grep || echo "No exabgp process found in ps"
if ! kill -0 $EXABGP_PID 2>/dev/null; then
echo "Failed to start ExaBGP. Checking logs:"
cat /app/logs/exabgp.log
echo "Trying to run ExaBGP directly for diagnosis:"
exabgp --version
echo "---"
echo "Direct execution test:"
timeout 10 exabgp /app/exabgp.conf || echo "Direct execution completed"
exit 1
fi
# Сохранение PID
echo $EXABGP_PID > /app/logs/exabgp.pid
# Сохранение PID файлов
echo "Saving PID files..."
pgrep zebra > /app/logs/zebra.pid 2>/dev/null || echo "zebra PID not found"
pgrep bgpd > /app/logs/bgpd.pid 2>/dev/null || echo "bgpd PID not found"
# Ожидание запуска
sleep 5
# Проверка статуса
if kill -0 $EXABGP_PID 2>/dev/null; then
echo "ExaBGP started successfully with PID: $EXABGP_PID"
echo "Recent logs:"
tail -10 /app/logs/exabgp.log
# Проверка статуса через vtysh
echo "Checking FRR status via vtysh..."
if vtysh -c "show version" > /dev/null 2>&1; then
echo "FRR started successfully"
echo "FRR version:"
vtysh -c "show version" 2>/dev/null || echo "Could not get version"
# Дополнительная проверка через несколько секунд
sleep 3
if kill -0 $EXABGP_PID 2>/dev/null; then
echo "ExaBGP is still running after 3 seconds"
echo "Process details:"
ps aux | grep exabgp | grep -v grep || echo "Process not found in ps output"
else
echo "ExaBGP process died after startup"
echo "Full logs:"
cat /app/logs/exabgp.log
exit 1
fi
echo "BGP neighbors:"
vtysh -c "show ip bgp neighbors" 2>/dev/null || echo "Could not get BGP neighbors"
echo "BGP summary:"
vtysh -c "show ip bgp summary" 2>/dev/null || echo "Could not get BGP summary"
else
echo "Failed to start ExaBGP"
echo "Full logs:"
cat /app/logs/exabgp.log
echo "---"
echo "Process status:"
ps aux | grep exabgp || echo "No exabgp process found"
echo "Failed to start FRR. Checking logs:"
cat /app/logs/frr.log 2>/dev/null || echo "No FRR logs found"
exit 1
fi
# Запуск скрипта загрузки префиксов в фоновом режиме
echo "Starting prefix loader in background..."
python3 /app/scripts/load_prefixes.py > /app/logs/prefixes.log 2>&1 &
PREFIX_LOADER_PID=$!
echo $PREFIX_LOADER_PID > /app/logs/prefix_loader.pid
# Запуск монитора в фоновом режиме
echo "Starting monitor in background..."
python3 /app/scripts/monitor.py > /app/logs/monitor.log 2>&1 &
MONITOR_PID=$!
echo $MONITOR_PID > /app/logs/monitor.pid
echo "All services started successfully!"
echo "Recent logs:"
tail -10 /app/logs/prefixes.log 2>/dev/null || echo "No prefix logs yet"
echo "---"
# Функция для graceful shutdown
cleanup() {
echo "Shutting down BGP server..."
if [ -f "/app/logs/exabgp.pid" ]; then
kill $(cat /app/logs/exabgp.pid) 2>/dev/null || true
echo "Received shutdown signal. Cleaning up..."
# Остановка скриптов
if [ -f "/app/logs/prefix_loader.pid" ]; then
kill $(cat /app/logs/prefix_loader.pid) 2>/dev/null || true
fi
if [ -f "/app/logs/monitor.pid" ]; then
kill $(cat /app/logs/monitor.pid) 2>/dev/null || true
fi
# Отзыв всех префиксов
echo "Withdrawing all prefixes..."
python3 /app/scripts/load_prefixes.py --withdraw > /dev/null 2>&1 || true
# Остановка FRR демонов
echo "Stopping FRR daemons..."
killall bgpd 2>/dev/null || true
killall zebra 2>/dev/null || true
echo "Cleanup completed."
exit 0
}
# Обработка сигналов
# Установка обработчиков сигналов
trap cleanup SIGTERM SIGINT
# Ожидание завершения
echo "Waiting for ExaBGP to run..."
echo "Process status:"
ps aux | grep exabgp || echo "No exabgp process found"
echo "---"
# Проверка процесса загрузки префиксов
echo "Checking prefix loader process:"
ps aux | grep load_prefixes || echo "No prefix loader process found"
echo "---"
wait $EXABGP_PID
echo "BGP Server is running. Press Ctrl+C to stop."
while true; do
sleep 10
# Проверка что демоны все еще работают
if ! pgrep zebra > /dev/null; then
echo "ERROR: zebra daemon stopped unexpectedly"
exit 1
fi
if ! pgrep bgpd > /dev/null; then
echo "ERROR: bgpd daemon stopped unexpectedly"
exit 1
fi
done
+54 -21
View File
@@ -1,33 +1,66 @@
# PowerShell скрипт для остановки BGP сервера
# Скрипт остановки BGP Server (FRR)
Write-Host "=== Stopping BGP Server ===" -ForegroundColor Yellow
param(
[string]$ContainerName = "bgp-server",
[switch]$Force
)
# Проверка что контейнер запущен
$containerStatus = docker ps --filter name=bgp-server --format "{{.Status}}"
if (!$containerStatus) {
Write-Host "BGP server is not running" -ForegroundColor Yellow
Write-Host "=== BGP Server (FRR) Stop Script ===" -ForegroundColor Green
# Проверка существования контейнера
$containerExists = docker ps -a --filter "name=$ContainerName" --format "table {{.Names}}" | Select-String $ContainerName
if (-not $containerExists) {
Write-Host "Container $ContainerName not found." -ForegroundColor Yellow
exit 0
}
Write-Host "BGP server is running. Stopping..." -ForegroundColor Yellow
# Проверка статуса контейнера
$containerRunning = docker ps --filter "name=$ContainerName" --format "table {{.Names}}" | Select-String $ContainerName
# Остановка контейнера
docker-compose down
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ BGP server stopped successfully" -ForegroundColor Green
if ($containerRunning) {
Write-Host "Container $ContainerName is running. Stopping..." -ForegroundColor Yellow
if (-not $Force) {
$response = Read-Host "Do you want to stop the container? (y/N)"
if ($response -ne "y" -and $response -ne "Y") {
Write-Host "Operation cancelled." -ForegroundColor Red
exit 0
}
}
# Graceful shutdown - отзыв префиксов
Write-Host "Withdrawing prefixes..." -ForegroundColor Yellow
docker exec $ContainerName python3 /app/scripts/load_prefixes.py --withdraw 2>$null
# Остановка контейнера
Write-Host "Stopping container..." -ForegroundColor Yellow
docker stop $ContainerName
if ($LASTEXITCODE -eq 0) {
Write-Host "Container stopped successfully!" -ForegroundColor Green
} else {
Write-Host "Failed to stop container." -ForegroundColor Red
exit 1
}
} else {
Write-Host "✗ Failed to stop BGP server" -ForegroundColor Red
exit 1
Write-Host "Container $ContainerName is not running." -ForegroundColor Yellow
}
# Проверка что контейнер остановлен
$containerStatus = docker ps --filter name=bgp-server --format "{{.Status}}"
if (!$containerStatus) {
Write-Host "✓ Container is stopped" -ForegroundColor Green
# Удаление контейнера если требуется
if ($Force) {
Write-Host "Removing container..." -ForegroundColor Yellow
docker rm $ContainerName 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Host "Container removed successfully!" -ForegroundColor Green
} else {
Write-Host "Container was already removed or removal failed." -ForegroundColor Yellow
}
} else {
Write-Host "Container is still running" -ForegroundColor Red
exit 1
Write-Host "Container stopped but not removed." -ForegroundColor Cyan
Write-Host "To remove container, run: docker rm $ContainerName" -ForegroundColor Cyan
Write-Host "Or use -Force parameter to stop and remove in one command." -ForegroundColor Cyan
}
Write-Host "=== BGP Server Stopped ===" -ForegroundColor Green
Write-Host "BGP Server (FRR) stopped successfully!" -ForegroundColor Green