253 lines
5.6 KiB
Python
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]]
|