186 lines
6.4 KiB
Python
186 lines
6.4 KiB
Python
"""Database models and session management."""
|
|
from datetime import datetime
|
|
from typing import Optional, List
|
|
import json
|
|
|
|
from sqlalchemy import (
|
|
create_engine, Column, Integer, String, Boolean,
|
|
DateTime, Float, ForeignKey, Text, event
|
|
)
|
|
from sqlalchemy.orm import declarative_base, sessionmaker, relationship, Session
|
|
from sqlalchemy.pool import StaticPool
|
|
|
|
from backend.config import settings, DATA_DIR
|
|
|
|
# Create engine
|
|
if settings.DATABASE_URL.startswith("sqlite"):
|
|
engine = create_engine(
|
|
settings.DATABASE_URL,
|
|
connect_args={"check_same_thread": False},
|
|
poolclass=StaticPool,
|
|
)
|
|
else:
|
|
engine = create_engine(settings.DATABASE_URL)
|
|
|
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
Base = declarative_base()
|
|
|
|
|
|
# Database Models
|
|
class User(Base):
|
|
"""Admin user model."""
|
|
__tablename__ = "users"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
username = Column(String(50), unique=True, index=True, nullable=False)
|
|
hashed_password = Column(String(255), nullable=False)
|
|
is_active = Column(Boolean, default=True)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
last_login = Column(DateTime, nullable=True)
|
|
|
|
|
|
class Server(Base):
|
|
"""IPMI Server model."""
|
|
__tablename__ = "servers"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
name = Column(String(100), nullable=False)
|
|
host = Column(String(100), nullable=False) # IP address
|
|
port = Column(Integer, default=623)
|
|
username = Column(String(100), nullable=False)
|
|
encrypted_password = Column(String(255), nullable=False) # Encrypted password
|
|
|
|
# Server type (dell, hpe, etc.)
|
|
vendor = Column(String(50), default="dell")
|
|
|
|
# Fan control settings
|
|
manual_control_enabled = Column(Boolean, default=False)
|
|
third_party_pcie_response = Column(Boolean, default=True)
|
|
fan_curve_data = Column(Text, nullable=True) # JSON string
|
|
auto_control_enabled = Column(Boolean, default=False)
|
|
|
|
# Panic mode settings
|
|
panic_mode_enabled = Column(Boolean, default=True)
|
|
panic_timeout_seconds = Column(Integer, default=60)
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
last_seen = Column(DateTime, nullable=True)
|
|
is_active = Column(Boolean, default=True)
|
|
|
|
# Relationships
|
|
sensor_data = relationship("SensorData", back_populates="server", cascade="all, delete-orphan")
|
|
fan_data = relationship("FanData", back_populates="server", cascade="all, delete-orphan")
|
|
|
|
|
|
class FanCurve(Base):
|
|
"""Fan curve configuration."""
|
|
__tablename__ = "fan_curves"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
server_id = Column(Integer, ForeignKey("servers.id"), nullable=False)
|
|
name = Column(String(100), default="Default")
|
|
curve_data = Column(Text, nullable=False) # JSON array of {temp, speed} points
|
|
sensor_source = Column(String(50), default="cpu") # cpu, inlet, exhaust, etc.
|
|
is_active = Column(Boolean, default=True)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
|
|
class SensorData(Base):
|
|
"""Historical sensor data."""
|
|
__tablename__ = "sensor_data"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
server_id = Column(Integer, ForeignKey("servers.id"), nullable=False)
|
|
sensor_name = Column(String(100), nullable=False)
|
|
sensor_type = Column(String(50), nullable=False) # temperature, voltage, fan, power
|
|
value = Column(Float, nullable=False)
|
|
unit = Column(String(20), nullable=True)
|
|
timestamp = Column(DateTime, default=datetime.utcnow)
|
|
|
|
server = relationship("Server", back_populates="sensor_data")
|
|
|
|
|
|
class FanData(Base):
|
|
"""Historical fan speed data."""
|
|
__tablename__ = "fan_data"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
server_id = Column(Integer, ForeignKey("servers.id"), nullable=False)
|
|
fan_number = Column(Integer, nullable=False)
|
|
fan_id = Column(String(20), nullable=False) # IPMI fan ID (0x00, 0x01, etc.)
|
|
speed_rpm = Column(Integer, nullable=True)
|
|
speed_percent = Column(Integer, nullable=True)
|
|
is_manual = Column(Boolean, default=False)
|
|
timestamp = Column(DateTime, default=datetime.utcnow)
|
|
|
|
server = relationship("Server", back_populates="fan_data")
|
|
|
|
|
|
class SystemLog(Base):
|
|
"""System event logs."""
|
|
__tablename__ = "system_logs"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
server_id = Column(Integer, ForeignKey("servers.id"), nullable=True)
|
|
event_type = Column(String(50), nullable=False) # panic, fan_change, error, warning, info
|
|
message = Column(Text, nullable=False)
|
|
details = Column(Text, nullable=True)
|
|
timestamp = Column(DateTime, default=datetime.utcnow)
|
|
|
|
|
|
class AppSettings(Base):
|
|
"""Application settings storage."""
|
|
__tablename__ = "app_settings"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
key = Column(String(100), unique=True, nullable=False)
|
|
value = Column(Text, nullable=True)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
|
|
def get_db() -> Session:
|
|
"""Get database session."""
|
|
db = SessionLocal()
|
|
try:
|
|
yield db
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
def init_db():
|
|
"""Initialize database tables."""
|
|
Base.metadata.create_all(bind=engine)
|
|
|
|
# Create default admin if no users exist
|
|
db = SessionLocal()
|
|
try:
|
|
# Check if setup is complete
|
|
setup_complete = db.query(AppSettings).filter(AppSettings.key == "setup_complete").first()
|
|
if not setup_complete:
|
|
AppSettings(key="setup_complete", value="false")
|
|
db.add(AppSettings(key="setup_complete", value="false"))
|
|
db.commit()
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
def is_setup_complete(db: Session) -> bool:
|
|
"""Check if initial setup is complete."""
|
|
setting = db.query(AppSettings).filter(AppSettings.key == "setup_complete").first()
|
|
if setting:
|
|
return setting.value == "true"
|
|
return False
|
|
|
|
|
|
def set_setup_complete(db: Session, complete: bool = True):
|
|
"""Mark setup as complete."""
|
|
setting = db.query(AppSettings).filter(AppSettings.key == "setup_complete").first()
|
|
if setting:
|
|
setting.value = "true" if complete else "false"
|
|
else:
|
|
setting = AppSettings(key="setup_complete", value="true" if complete else "false")
|
|
db.add(setting)
|
|
db.commit()
|