mirror of
https://github.com/taylanbakircioglu/haproxy-openmanager.git
synced 2026-09-19 09:05:43 +00:00
cb645b5ef9
🔴 PRODUCTION CRITICAL FIX - Agent HTTP 422 Validation Error PROBLEM: - Production agents sending heartbeat with flat system_info fields - Backend Pydantic model was strict and rejecting unknown fields - Agents going offline with 'Validation error in request data' (HTTP 422) ROOT CAUSE: - Legacy agents embed system_info as flat key-value pairs in heartbeat JSON - Backend expected only defined fields, rejected extra fields - No backward compatibility for agent format variations SOLUTION - BACKEND ONLY (NO AGENT CHANGES): ✅ Added 'extra = "allow"' to AgentHeartbeat Pydantic Config ✅ Backend now accepts both formats: - Flat format: operating_system, kernel_version, etc. (legacy agents) - Nested format: system_info: {...} (future agents) ✅ Updated comments to clarify backward compatibility IMPACT: - ✅ ZERO CHANGES to production agent scripts - ✅ Existing agents will work immediately after backend deploy - ✅ Forward compatible with future agent upgrades - ✅ Tolerant to agent format variations SAFETY: - Minimal change (3 lines) - Pydantic still validates required fields - Extra fields ignored silently (no breaking changes) - Production agents continue without restart or upgrade DEPLOYMENT: 1. Deploy backend (this commit) 2. Agents come online automatically (no action needed) 3. Agent upgrades can happen later (when convenient) This fix ensures production stability without touching agent scripts.
116 lines
3.7 KiB
Python
116 lines
3.7 KiB
Python
from pydantic import BaseModel
|
|
from typing import Optional, List, Dict, Any
|
|
|
|
class AgentCreate(BaseModel):
|
|
name: str
|
|
pool_id: int
|
|
platform: str = "linux"
|
|
architecture: str = "amd64"
|
|
version: str = "1.0.0"
|
|
|
|
class AgentRegister(BaseModel):
|
|
name: str
|
|
pool_id: int
|
|
platform: str = "linux"
|
|
architecture: str = "amd64"
|
|
version: str = "1.0.0"
|
|
hostname: Optional[str] = None
|
|
ip_address: Optional[str] = None
|
|
operating_system: Optional[str] = None
|
|
kernel_version: Optional[str] = None
|
|
uptime: Optional[int] = None
|
|
cpu_count: Optional[int] = None
|
|
memory_total: Optional[int] = None # in MB
|
|
disk_space: Optional[int] = None # in MB
|
|
network_interfaces: Optional[List[str]] = None
|
|
capabilities: Optional[List[str]] = None
|
|
|
|
class AgentHeartbeat(BaseModel):
|
|
name: str
|
|
status: str = "online"
|
|
|
|
# Optional system info from agent
|
|
hostname: Optional[str] = None
|
|
platform: Optional[str] = None
|
|
architecture: Optional[str] = None
|
|
version: Optional[str] = None # Agent version
|
|
cluster_id: Optional[int] = None
|
|
|
|
# Detailed system information (flat format - for backward compatibility with old agents)
|
|
operating_system: Optional[str] = None
|
|
kernel_version: Optional[str] = None
|
|
uptime: Optional[int] = None # in seconds
|
|
cpu_count: Optional[int] = None
|
|
memory_total: Optional[int] = None # in bytes
|
|
disk_space: Optional[int] = None # in bytes
|
|
network_interfaces: Optional[List[str]] = None
|
|
capabilities: Optional[List[str]] = None
|
|
ip_address: Optional[str] = None
|
|
|
|
# Optional performance metrics
|
|
cpu_usage: Optional[float] = None
|
|
memory_usage: Optional[float] = None
|
|
disk_usage: Optional[float] = None
|
|
load_average: Optional[List[float]] = None
|
|
network_io: Optional[Dict[str, Any]] = None
|
|
|
|
# HAProxy info
|
|
haproxy_status: Optional[str] = None
|
|
haproxy_version: Optional[str] = None
|
|
|
|
# Server status from HAProxy stats socket
|
|
server_statuses: Optional[Dict[str, Dict[str, str]]] = None # {backend_name: {server_name: status}}
|
|
|
|
# Full HAProxy stats CSV (base64 encoded) for dashboard metrics
|
|
haproxy_stats_csv: Optional[str] = None
|
|
|
|
# Config sync tracking (agent reports what it successfully applied)
|
|
applied_config_version: Optional[str] = None
|
|
|
|
# System info collected by agent (JSON object - nested format for new agents)
|
|
system_info: Optional[Dict[str, Any]] = None
|
|
|
|
# Other optional fields
|
|
last_config_update: Optional[str] = None
|
|
errors: Optional[List[str]] = None
|
|
|
|
class Config:
|
|
# Allow extra fields from agents (for forward compatibility and tolerance)
|
|
extra = "allow"
|
|
|
|
class AgentToggle(BaseModel):
|
|
enabled: bool
|
|
|
|
class AgentPoolCreate(BaseModel):
|
|
name: str
|
|
description: Optional[str] = None
|
|
environment: str = "development" # development, staging, production
|
|
location: Optional[str] = None
|
|
default_config: Optional[Dict[str, Any]] = None
|
|
is_active: Optional[bool] = True
|
|
|
|
class AgentPoolUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
description: Optional[str] = None
|
|
environment: Optional[str] = None
|
|
location: Optional[str] = None
|
|
default_config: Optional[Dict[str, Any]] = None
|
|
is_active: Optional[bool] = None
|
|
|
|
# Aliases for pool management
|
|
PoolCreate = AgentPoolCreate
|
|
PoolUpdate = AgentPoolUpdate
|
|
|
|
class AgentScriptRequest(BaseModel):
|
|
platform: str
|
|
architecture: str
|
|
pool_id: int
|
|
cluster_id: int # ✅ FIXED: Added cluster_id field
|
|
agent_name: str
|
|
hostname_prefix: str
|
|
haproxy_bin_path: str
|
|
haproxy_config_path: str
|
|
stats_socket_path: str
|
|
|
|
class AgentUpgradeRequest(BaseModel):
|
|
agent_id: int |