ipmi-fan-control/backend/schemas.py

253 lines
5.6 KiB
Python

"""Pydantic schemas for API requests and responses."""
from datetime import datetime
from typing import List, Optional, Dict, Any, Union
from pydantic import BaseModel, Field, validator
# User schemas
class UserBase(BaseModel):
username: str
class UserCreate(UserBase):
password: str = Field(..., min_length=8)
class UserLogin(UserBase):
password: str
class UserResponse(UserBase):
id: int
is_active: bool
created_at: datetime
last_login: Optional[datetime]
class Config:
from_attributes = True
# Token schemas
class Token(BaseModel):
access_token: str
token_type: str = "bearer"
class TokenData(BaseModel):
username: Optional[str] = None
# Fan curve schemas
class FanCurvePoint(BaseModel):
temp: float = Field(..., ge=0, le=150, description="Temperature in Celsius")
speed: int = Field(..., ge=0, le=100, description="Fan speed percentage")
class FanCurveBase(BaseModel):
name: str = "Default"
curve_data: List[FanCurvePoint]
sensor_source: str = "cpu"
is_active: bool = True
class FanCurveCreate(FanCurveBase):
pass
class FanCurveResponse(FanCurveBase):
id: int
server_id: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
# Server schemas
class ServerBase(BaseModel):
name: str
host: str
port: int = 623
username: str
vendor: str = "dell"
@validator('vendor')
def validate_vendor(cls, v):
allowed = ['dell', 'hpe', 'supermicro', 'other']
if v.lower() not in allowed:
raise ValueError(f'Vendor must be one of: {allowed}')
return v.lower()
class ServerCreate(ServerBase):
password: str
@validator('port')
def validate_port(cls, v):
if not 1 <= v <= 65535:
raise ValueError('Port must be between 1 and 65535')
return v
class ServerUpdate(BaseModel):
name: Optional[str] = None
host: Optional[str] = None
port: Optional[int] = None
username: Optional[str] = None
password: Optional[str] = None
vendor: Optional[str] = None
manual_control_enabled: Optional[bool] = None
third_party_pcie_response: Optional[bool] = None
auto_control_enabled: Optional[bool] = None
panic_mode_enabled: Optional[bool] = None
panic_timeout_seconds: Optional[int] = None
class ServerResponse(ServerBase):
id: int
manual_control_enabled: bool
third_party_pcie_response: bool
auto_control_enabled: bool
panic_mode_enabled: bool
panic_timeout_seconds: int
created_at: datetime
updated_at: datetime
last_seen: Optional[datetime]
is_active: bool
class Config:
from_attributes = True
class ServerDetailResponse(ServerResponse):
fan_curves: List[FanCurveResponse] = []
class ServerStatusResponse(BaseModel):
server: ServerResponse
is_connected: bool
controller_status: Dict[str, Any]
# Sensor schemas
class SensorReading(BaseModel):
name: str
sensor_type: str
value: float
unit: str
status: str
class TemperatureReading(BaseModel):
name: str
location: str
value: float
status: str
class FanReading(BaseModel):
fan_id: str
fan_number: int
speed_rpm: Optional[int]
speed_percent: Optional[int]
class SensorDataResponse(BaseModel):
id: int
sensor_name: str
sensor_type: str
value: float
unit: Optional[str]
timestamp: datetime
class Config:
from_attributes = True
class FanDataResponse(BaseModel):
id: int
fan_number: int
fan_id: str
speed_rpm: Optional[int]
speed_percent: Optional[int]
is_manual: bool
timestamp: datetime
class Config:
from_attributes = True
class ServerSensorsResponse(BaseModel):
server_id: int
temperatures: List[TemperatureReading]
fans: List[FanReading]
all_sensors: List[SensorReading]
timestamp: datetime
# Fan control schemas
class FanControlCommand(BaseModel):
fan_id: str = Field(default="0xff", description="Fan ID (0xff for all, 0x00-0x07 for specific)")
speed_percent: int = Field(..., ge=0, le=100, description="Fan speed percentage")
class FanCurveApply(BaseModel):
curve_id: Optional[int] = None
curve_data: Optional[List[FanCurvePoint]] = None
sensor_source: Optional[str] = "cpu"
class AutoControlSettings(BaseModel):
enabled: bool
curve_id: Optional[int] = None
# System log schemas
class SystemLogResponse(BaseModel):
id: int
server_id: Optional[int]
event_type: str
message: str
details: Optional[str]
timestamp: datetime
class Config:
from_attributes = True
# Setup wizard schemas
class SetupStatus(BaseModel):
setup_complete: bool
class SetupComplete(BaseModel):
username: str = Field(..., min_length=3, max_length=50)
password: str = Field(..., min_length=8)
confirm_password: str = Field(..., min_length=8)
@validator('confirm_password')
def passwords_match(cls, v, values):
if 'password' in values and v != values['password']:
raise ValueError('Passwords do not match')
return v
# Dashboard schemas
class DashboardStats(BaseModel):
total_servers: int
active_servers: int
manual_control_servers: int
auto_control_servers: int
panic_mode_servers: int
recent_logs: List[SystemLogResponse]
class ServerDashboardData(BaseModel):
server: ServerResponse
current_temperatures: List[TemperatureReading]
current_fans: List[FanReading]
recent_sensor_data: List[SensorDataResponse]
recent_fan_data: List[FanDataResponse]
power_consumption: Optional[Dict[str, str]]