0740016ad8
Co-authored-by: Cursor <cursoragent@cursor.com>
144 lines
5.4 KiB
Python
144 lines
5.4 KiB
Python
"""机房大客户申请,以及网段内禁止混入 10 系显卡、硬件指纹去重。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
from sqlalchemy import case, select
|
|
from sqlalchemy.orm import Session, selectinload
|
|
|
|
from app.clock import utcnow
|
|
from app.errors import AppError
|
|
from app.models import ComputeNode, DatacenterApplication, DatacenterStatus
|
|
from app.services.hardware import GENERATION_10, GENERATION_30, GENERATION_40, classify_generation
|
|
from app.services.ip_guard import DATACENTER_BATCH_MESSAGE, add_whitelist_ip, matching_whitelist, normalize_admin_ip
|
|
|
|
MIN_DATACENTER_NODES = 5
|
|
FINGERPRINT_CLASH = "硬件指纹重复,疑似虚拟机克隆或刷量"
|
|
_FINGERPRINT = re.compile(r"^[a-f0-9]{64}$")
|
|
_LOW_END = re.compile(r"10\s*系|20\s*系|GTX|核显|1050|1060|1070|1080|1650|1660|2060|2070|2080", re.IGNORECASE)
|
|
|
|
|
|
def assert_fingerprint(session: Session, fingerprint: str | None, node_id: int | None = None) -> str | None:
|
|
"""同一块物理机不能挂第二台节点。空指纹不占唯一索引。"""
|
|
text = (fingerprint or "").strip().lower()
|
|
if not text:
|
|
return None
|
|
if _FINGERPRINT.fullmatch(text) is None:
|
|
raise AppError("硬件指纹格式不正确")
|
|
query = select(ComputeNode.id).where(ComputeNode.hardware_fingerprint == text)
|
|
if node_id is not None:
|
|
query = query.where(ComputeNode.id != node_id)
|
|
if session.scalar(query) is not None:
|
|
raise AppError(FINGERPRINT_CLASH, 409)
|
|
return text
|
|
|
|
|
|
def assert_subnet_gpu(session: Session, ip: str, gpu_model: str | None) -> None:
|
|
"""已经批准的机房网段里,不允许再挂 10 系及更低的卡。"""
|
|
address = (ip or "").strip()
|
|
if not address or "/" in address:
|
|
return
|
|
entry = matching_whitelist(session, address)
|
|
if entry is None or "/" not in entry:
|
|
return
|
|
model = (gpu_model or "").strip()
|
|
if not model:
|
|
return
|
|
generation, _days = classify_generation(model)
|
|
if generation == GENERATION_10:
|
|
raise AppError(DATACENTER_BATCH_MESSAGE, 409)
|
|
|
|
|
|
def submit_application(
|
|
session: Session,
|
|
user_id: int,
|
|
contact: str,
|
|
node_count: int,
|
|
subnet_ip: str,
|
|
hardware_proof: str,
|
|
gpu_generation: str,
|
|
) -> DatacenterApplication:
|
|
subnet = _require_subnet(subnet_ip)
|
|
_require_threshold(node_count, gpu_generation, hardware_proof)
|
|
existing = session.scalar(
|
|
select(DatacenterApplication.id).where(
|
|
DatacenterApplication.subnet_ip == subnet,
|
|
DatacenterApplication.status.in_((DatacenterStatus.PENDING, DatacenterStatus.APPROVED)),
|
|
)
|
|
)
|
|
if existing is not None:
|
|
raise AppError("这个网段已有待审核或已通过的机房申请", 409)
|
|
row = DatacenterApplication(
|
|
user_id=user_id,
|
|
contact=contact.strip(),
|
|
node_count=node_count,
|
|
subnet_ip=subnet,
|
|
hardware_proof=hardware_proof.strip(),
|
|
gpu_generation=gpu_generation,
|
|
status=DatacenterStatus.PENDING,
|
|
created_at=utcnow(),
|
|
)
|
|
session.add(row)
|
|
session.flush()
|
|
return row
|
|
|
|
|
|
def list_applications(session: Session, user_id: int | None = None) -> list[DatacenterApplication]:
|
|
pending_first = case((DatacenterApplication.status == DatacenterStatus.PENDING, 0), else_=1)
|
|
query = select(DatacenterApplication).options(selectinload(DatacenterApplication.user))
|
|
if user_id is not None:
|
|
query = query.where(DatacenterApplication.user_id == user_id)
|
|
return list(session.scalars(query.order_by(pending_first, DatacenterApplication.id.desc())).all())
|
|
|
|
|
|
def approve_application(session: Session, application_id: int) -> DatacenterApplication:
|
|
row = _pending(session, application_id)
|
|
_require_threshold(row.node_count, row.gpu_generation, row.hardware_proof)
|
|
try:
|
|
add_whitelist_ip(session, row.subnet_ip, note=f"机房申请 #{row.id} {row.contact}")
|
|
except AppError as exc:
|
|
if exc.status_code != 409:
|
|
raise
|
|
row.status = DatacenterStatus.APPROVED
|
|
row.reviewed_at = utcnow()
|
|
session.flush()
|
|
return row
|
|
|
|
|
|
def reject_application(session: Session, application_id: int, note: str | None = None) -> DatacenterApplication:
|
|
row = _pending(session, application_id)
|
|
row.status = DatacenterStatus.REJECTED
|
|
row.note = (note or "").strip() or None
|
|
row.reviewed_at = utcnow()
|
|
session.flush()
|
|
return row
|
|
|
|
|
|
def _pending(session: Session, application_id: int) -> DatacenterApplication:
|
|
row = session.scalar(
|
|
select(DatacenterApplication)
|
|
.options(selectinload(DatacenterApplication.user))
|
|
.where(DatacenterApplication.id == application_id)
|
|
)
|
|
if row is None:
|
|
raise AppError("机房申请不存在", 404)
|
|
if row.status != DatacenterStatus.PENDING:
|
|
raise AppError("只有待审核的机房申请可以处理", 409)
|
|
return row
|
|
|
|
|
|
def _require_subnet(value: str) -> str:
|
|
text = value.strip()
|
|
if "/" not in text:
|
|
raise AppError("请填写机房出口网段,例如 203.0.113.0/24")
|
|
return normalize_admin_ip(text)
|
|
|
|
|
|
def _require_threshold(node_count: int, gpu_generation: str, hardware_proof: str) -> None:
|
|
generation = (gpu_generation or "").strip()
|
|
if node_count < MIN_DATACENTER_NODES or generation not in {GENERATION_30, GENERATION_40}:
|
|
raise AppError(DATACENTER_BATCH_MESSAGE, 400)
|
|
if _LOW_END.search(hardware_proof or ""):
|
|
raise AppError(DATACENTER_BATCH_MESSAGE, 400)
|