202 lines
7.9 KiB
Python
202 lines
7.9 KiB
Python
"""
|
|
Linux BenchTools - Benchmark API
|
|
"""
|
|
|
|
import json
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
from datetime import datetime
|
|
|
|
from app.db.session import get_db
|
|
from app.core.security import verify_token
|
|
from app.schemas.benchmark import BenchmarkPayload, BenchmarkResponse, BenchmarkDetail, BenchmarkSummary
|
|
from app.models.device import Device
|
|
from app.models.hardware_snapshot import HardwareSnapshot
|
|
from app.models.benchmark import Benchmark
|
|
from app.utils.scoring import calculate_global_score
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.post("/benchmark", response_model=BenchmarkResponse, status_code=status.HTTP_200_OK)
|
|
async def submit_benchmark(
|
|
payload: BenchmarkPayload,
|
|
db: Session = Depends(get_db),
|
|
_: bool = Depends(verify_token)
|
|
):
|
|
"""
|
|
Submit a benchmark result from a client machine.
|
|
|
|
This endpoint:
|
|
1. Resolves or creates the device
|
|
2. Creates a hardware snapshot
|
|
3. Creates a benchmark record
|
|
4. Returns device_id and benchmark_id
|
|
"""
|
|
|
|
# 1. Resolve or create device
|
|
device = db.query(Device).filter(Device.hostname == payload.device_identifier).first()
|
|
|
|
if not device:
|
|
device = Device(
|
|
hostname=payload.device_identifier,
|
|
created_at=datetime.utcnow(),
|
|
updated_at=datetime.utcnow()
|
|
)
|
|
db.add(device)
|
|
db.flush() # Get device.id
|
|
|
|
# Update device timestamp
|
|
device.updated_at = datetime.utcnow()
|
|
|
|
# 2. Create hardware snapshot
|
|
hw = payload.hardware
|
|
snapshot = HardwareSnapshot(
|
|
device_id=device.id,
|
|
captured_at=datetime.utcnow(),
|
|
|
|
# CPU
|
|
cpu_vendor=hw.cpu.vendor if hw.cpu else None,
|
|
cpu_model=hw.cpu.model if hw.cpu else None,
|
|
cpu_microarchitecture=hw.cpu.microarchitecture if hw.cpu else None,
|
|
cpu_cores=hw.cpu.cores if hw.cpu else None,
|
|
cpu_threads=hw.cpu.threads if hw.cpu else None,
|
|
cpu_base_freq_ghz=hw.cpu.base_freq_ghz if hw.cpu else None,
|
|
cpu_max_freq_ghz=hw.cpu.max_freq_ghz if hw.cpu else None,
|
|
cpu_cache_l1_kb=hw.cpu.cache_l1_kb if hw.cpu else None,
|
|
cpu_cache_l2_kb=hw.cpu.cache_l2_kb if hw.cpu else None,
|
|
cpu_cache_l3_kb=hw.cpu.cache_l3_kb if hw.cpu else None,
|
|
cpu_flags=json.dumps(hw.cpu.flags) if hw.cpu and hw.cpu.flags else None,
|
|
cpu_tdp_w=hw.cpu.tdp_w if hw.cpu else None,
|
|
|
|
# RAM
|
|
ram_total_mb=hw.ram.total_mb if hw.ram else None,
|
|
ram_used_mb=hw.ram.used_mb if hw.ram else None, # NEW
|
|
ram_free_mb=hw.ram.free_mb if hw.ram else None, # NEW
|
|
ram_shared_mb=hw.ram.shared_mb if hw.ram else None, # NEW
|
|
ram_slots_total=hw.ram.slots_total if hw.ram else None,
|
|
ram_slots_used=hw.ram.slots_used if hw.ram else None,
|
|
ram_ecc=hw.ram.ecc if hw.ram else None,
|
|
ram_layout_json=json.dumps([slot.dict() for slot in hw.ram.layout]) if hw.ram and hw.ram.layout else None,
|
|
|
|
# GPU
|
|
gpu_summary=f"{hw.gpu.vendor} {hw.gpu.model}" if hw.gpu and hw.gpu.model else None,
|
|
gpu_vendor=hw.gpu.vendor if hw.gpu else None,
|
|
gpu_model=hw.gpu.model if hw.gpu else None,
|
|
gpu_driver_version=hw.gpu.driver_version if hw.gpu else None,
|
|
gpu_memory_dedicated_mb=hw.gpu.memory_dedicated_mb if hw.gpu else None,
|
|
gpu_memory_shared_mb=hw.gpu.memory_shared_mb if hw.gpu else None,
|
|
gpu_api_support=json.dumps(hw.gpu.api_support) if hw.gpu and hw.gpu.api_support else None,
|
|
|
|
# Storage
|
|
storage_summary=f"{len(hw.storage.devices)} device(s)" if hw.storage and hw.storage.devices else None,
|
|
storage_devices_json=json.dumps([d.dict() for d in hw.storage.devices]) if hw.storage and hw.storage.devices else None,
|
|
partitions_json=json.dumps([p.dict() for p in hw.storage.partitions]) if hw.storage and hw.storage.partitions else None,
|
|
|
|
# Network
|
|
network_interfaces_json=json.dumps([i.dict() for i in hw.network.interfaces]) if hw.network and hw.network.interfaces else None,
|
|
|
|
# OS / Motherboard
|
|
os_name=hw.os.name if hw.os else None,
|
|
os_version=hw.os.version if hw.os else None,
|
|
kernel_version=hw.os.kernel_version if hw.os else None,
|
|
architecture=hw.os.architecture if hw.os else None,
|
|
virtualization_type=hw.os.virtualization_type if hw.os else None,
|
|
motherboard_vendor=hw.motherboard.vendor if hw.motherboard else None,
|
|
motherboard_model=hw.motherboard.model if hw.motherboard else None,
|
|
bios_version=hw.motherboard.bios_version if hw.motherboard else None,
|
|
bios_date=hw.motherboard.bios_date if hw.motherboard else None,
|
|
|
|
# Misc
|
|
sensors_json=json.dumps(hw.sensors.dict()) if hw.sensors else None,
|
|
raw_info_json=json.dumps(hw.raw_info.dict()) if hw.raw_info else None
|
|
)
|
|
|
|
db.add(snapshot)
|
|
db.flush() # Get snapshot.id
|
|
|
|
# 3. Create benchmark
|
|
results = payload.results
|
|
|
|
# Calculate global score if not provided or recalculate
|
|
global_score = calculate_global_score(
|
|
cpu_score=results.cpu.score if results.cpu else None,
|
|
memory_score=results.memory.score if results.memory else None,
|
|
disk_score=results.disk.score if results.disk else None,
|
|
network_score=results.network.score if results.network else None,
|
|
gpu_score=results.gpu.score if results.gpu else None
|
|
)
|
|
|
|
# Use provided global_score if available and valid
|
|
if results.global_score is not None:
|
|
global_score = results.global_score
|
|
|
|
# Extract network results for easier frontend access
|
|
network_results = None
|
|
if results.network:
|
|
network_results = {
|
|
"upload_mbps": results.network.upload_mbps if hasattr(results.network, 'upload_mbps') else None,
|
|
"download_mbps": results.network.download_mbps if hasattr(results.network, 'download_mbps') else None,
|
|
"ping_ms": results.network.ping_ms if hasattr(results.network, 'ping_ms') else None,
|
|
"score": results.network.score
|
|
}
|
|
|
|
benchmark = Benchmark(
|
|
device_id=device.id,
|
|
hardware_snapshot_id=snapshot.id,
|
|
run_at=datetime.utcnow(),
|
|
bench_script_version=payload.bench_script_version,
|
|
|
|
global_score=global_score,
|
|
cpu_score=results.cpu.score if results.cpu else None,
|
|
memory_score=results.memory.score if results.memory else None,
|
|
disk_score=results.disk.score if results.disk else None,
|
|
network_score=results.network.score if results.network else None,
|
|
gpu_score=results.gpu.score if results.gpu else None,
|
|
|
|
details_json=json.dumps(results.dict()),
|
|
network_results_json=json.dumps(network_results) if network_results else None
|
|
)
|
|
|
|
db.add(benchmark)
|
|
db.commit()
|
|
|
|
return BenchmarkResponse(
|
|
status="ok",
|
|
device_id=device.id,
|
|
benchmark_id=benchmark.id,
|
|
message=f"Benchmark successfully recorded for device '{device.hostname}'"
|
|
)
|
|
|
|
|
|
@router.get("/benchmarks/{benchmark_id}", response_model=BenchmarkDetail)
|
|
async def get_benchmark(
|
|
benchmark_id: int,
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Get detailed benchmark information
|
|
"""
|
|
benchmark = db.query(Benchmark).filter(Benchmark.id == benchmark_id).first()
|
|
|
|
if not benchmark:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Benchmark {benchmark_id} not found"
|
|
)
|
|
|
|
return BenchmarkDetail(
|
|
id=benchmark.id,
|
|
device_id=benchmark.device_id,
|
|
hardware_snapshot_id=benchmark.hardware_snapshot_id,
|
|
run_at=benchmark.run_at.isoformat(),
|
|
bench_script_version=benchmark.bench_script_version,
|
|
global_score=benchmark.global_score,
|
|
cpu_score=benchmark.cpu_score,
|
|
memory_score=benchmark.memory_score,
|
|
disk_score=benchmark.disk_score,
|
|
network_score=benchmark.network_score,
|
|
gpu_score=benchmark.gpu_score,
|
|
details=json.loads(benchmark.details_json)
|
|
)
|