- Add real client IP detection (CF-Connecting-IP / src fallback) to both frontends - Add per-IP rate limiting (429) using real IP for Cloudflare compatibility - Add CrowdSec syslog forwarding with custom log format - Add httplog option for detailed HTTP logging - Fix Python-level defaults on MCP tool parameters to match Field(default=X) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
98 lines
3.5 KiB
Python
98 lines
3.5 KiB
Python
"""Monitoring tools for HAProxy MCP Server."""
|
|
|
|
from typing import Annotated
|
|
|
|
from pydantic import Field
|
|
|
|
from ..exceptions import HaproxyError
|
|
from ..validation import validate_backend_name
|
|
from ..haproxy_client import haproxy_cmd
|
|
from ..utils import parse_stat_csv
|
|
|
|
|
|
def register_monitoring_tools(mcp):
|
|
"""Register monitoring tools with MCP server."""
|
|
|
|
@mcp.tool()
|
|
def haproxy_stats() -> str:
|
|
"""Get HAProxy status and statistics.
|
|
|
|
Returns:
|
|
Key HAProxy metrics (name, version, uptime, connections, etc.)
|
|
"""
|
|
try:
|
|
result = haproxy_cmd("show info")
|
|
stats = {}
|
|
for line in result.split("\n"):
|
|
if ":" in line:
|
|
key, value = line.split(":", 1)
|
|
stats[key.strip()] = value.strip()
|
|
|
|
important = ["Name", "Version", "Uptime_sec", "CurrConns", "MaxConn", "Run_queue", "Tasks"]
|
|
output = []
|
|
for key in important:
|
|
if key in stats:
|
|
output.append(f"• {key}: {stats[key]}")
|
|
|
|
return "\n".join(output) if output else result
|
|
except HaproxyError as e:
|
|
return f"Error: {e}"
|
|
|
|
@mcp.tool()
|
|
def haproxy_backends() -> str:
|
|
"""List all HAProxy backends.
|
|
|
|
Returns:
|
|
List of all configured backend names
|
|
"""
|
|
try:
|
|
result = haproxy_cmd("show backend")
|
|
backends = [line for line in result.split("\n") if line and not line.startswith("#")]
|
|
return "Backends:\n" + "\n".join(f"• {b}" for b in backends)
|
|
except HaproxyError as e:
|
|
return f"Error: {e}"
|
|
|
|
@mcp.tool()
|
|
def haproxy_list_frontends() -> str:
|
|
"""List all HAProxy frontends with their status.
|
|
|
|
Returns:
|
|
List of frontends with status and session counts
|
|
"""
|
|
try:
|
|
result = haproxy_cmd("show stat")
|
|
frontends = []
|
|
for stat in parse_stat_csv(result):
|
|
if stat["svname"] == "FRONTEND":
|
|
frontends.append(
|
|
f"• {stat['pxname']}: {stat['status']} (sessions: {stat['scur']})"
|
|
)
|
|
if not frontends:
|
|
return "No frontends found"
|
|
return "Frontends:\n" + "\n".join(frontends)
|
|
except HaproxyError as e:
|
|
return f"Error: {e}"
|
|
|
|
@mcp.tool()
|
|
def haproxy_get_connections(
|
|
backend: Annotated[str, Field(default="", description="Optional: Backend name to filter (e.g., 'pool_1'). Empty = all backends")] = ""
|
|
) -> str:
|
|
"""Get active connection counts per server for monitoring traffic distribution."""
|
|
if backend and not validate_backend_name(backend):
|
|
return "Error: Invalid backend name (use alphanumeric, underscore, hyphen only)"
|
|
try:
|
|
result = haproxy_cmd("show stat")
|
|
connections = []
|
|
for stat in parse_stat_csv(result):
|
|
if backend and stat["pxname"] != backend:
|
|
continue
|
|
if stat["svname"] in ["FRONTEND", "BACKEND"]:
|
|
connections.append(
|
|
f"• {stat['pxname']} ({stat['svname']}): {stat['scur']} current, {stat['smax']} max"
|
|
)
|
|
elif stat["svname"]:
|
|
connections.append(f" - {stat['svname']}: {stat['scur']} connections")
|
|
return "\n".join(connections) if connections else "No connection data"
|
|
except HaproxyError as e:
|
|
return f"Error: {e}"
|