revert(nodes): remove server auto-setup feature
Drop the /api/servers/auto-setup endpoint, the AutoSetupDialog UI and the useAutoSetupServer hook. Rename the auto_setup locale strings to auto_create_token. Also normalize the storages response to an array. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -146,284 +146,6 @@ def create_proxmox_server(
|
||||
return server
|
||||
|
||||
|
||||
@router.post("/api/servers/auto-setup", status_code=status.HTTP_201_CREATED)
|
||||
async def auto_setup_proxmox_server(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.servers.add"))
|
||||
):
|
||||
"""
|
||||
Автоматическая настройка Proxmox сервера:
|
||||
1. Проверяем подключение по логину/паролю
|
||||
2. Создаем API Token автоматически
|
||||
3. Сохраняем сервер в базу с токеном и паролем (для VNC)
|
||||
"""
|
||||
import requests as http_requests
|
||||
import uuid
|
||||
|
||||
# Получаем данные из body
|
||||
data = await request.json()
|
||||
|
||||
name = data.get('name')
|
||||
hostname = data.get('hostname')
|
||||
ip_address = data.get('ip_address')
|
||||
port = data.get('port', 8006)
|
||||
api_user = data.get('api_user', 'root@pam')
|
||||
password = data.get('password')
|
||||
verify_ssl = data.get('verify_ssl', True)
|
||||
description = data.get('description')
|
||||
|
||||
if not all([name, ip_address, password]):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Необходимо указать: name, ip_address, password"
|
||||
)
|
||||
|
||||
# Разрешаем несколько серверов с одинаковым IP
|
||||
# Проверяем только дубликаты имён
|
||||
existing = db.query(ProxmoxServer).filter(ProxmoxServer.name == name).first()
|
||||
if existing:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Сервер с именем '{name}' уже существует"
|
||||
)
|
||||
|
||||
base_url = f"https://{ip_address}:{port}"
|
||||
|
||||
# 1. Получить auth ticket
|
||||
logger.info(f"Attempting to connect to Proxmox at {base_url}")
|
||||
try:
|
||||
auth_response = await _run_in_executor(
|
||||
http_requests.post,
|
||||
f"{base_url}/api2/json/access/ticket",
|
||||
data={"username": api_user, "password": password},
|
||||
verify=verify_ssl,
|
||||
timeout=15
|
||||
)
|
||||
|
||||
if auth_response.status_code != 200:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Ошибка авторизации в Proxmox: неверный логин или пароль"
|
||||
)
|
||||
|
||||
auth_data = auth_response.json().get("data", {})
|
||||
ticket = auth_data.get("ticket")
|
||||
csrf_token = auth_data.get("CSRFPreventionToken")
|
||||
|
||||
if not ticket:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Не удалось получить auth ticket от Proxmox"
|
||||
)
|
||||
|
||||
logger.info("Successfully authenticated to Proxmox")
|
||||
|
||||
except http_requests.exceptions.RequestException as e:
|
||||
logger.error(f"Connection error to Proxmox: {e}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
detail=f"Не удалось подключиться к Proxmox: {str(e)}"
|
||||
)
|
||||
|
||||
# 2. Создать API Token
|
||||
# Извлекаем username из api_user (root@pam -> root)
|
||||
username_part = api_user.split('@')[0] if '@' in api_user else api_user
|
||||
realm = api_user.split('@')[1] if '@' in api_user else 'pam'
|
||||
|
||||
token_name = f"panel-{uuid.uuid4().hex[:8]}"
|
||||
|
||||
headers = {
|
||||
"Cookie": f"PVEAuthCookie={ticket}",
|
||||
"CSRFPreventionToken": csrf_token
|
||||
}
|
||||
|
||||
try:
|
||||
# Создаем токен через API
|
||||
token_response = await _run_in_executor(
|
||||
http_requests.post,
|
||||
f"{base_url}/api2/json/access/users/{api_user}/token/{token_name}",
|
||||
headers=headers,
|
||||
data={"privsep": "0"}, # Полные права как у пользователя
|
||||
verify=verify_ssl,
|
||||
timeout=15
|
||||
)
|
||||
|
||||
if token_response.status_code not in [200, 201]:
|
||||
logger.warning(f"Failed to create token: {token_response.status_code} - {token_response.text}")
|
||||
# Если не удалось создать токен, работаем только с паролем
|
||||
token_value = None
|
||||
token_name = None
|
||||
logger.info("Will use password authentication only")
|
||||
else:
|
||||
token_data = token_response.json().get("data", {})
|
||||
token_value = token_data.get("value")
|
||||
logger.info("Created API token successfully")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error creating token: {e}, will use password auth")
|
||||
token_value = None
|
||||
token_name = None
|
||||
|
||||
# 3. Получить список нод кластера и их IP адреса
|
||||
cluster_nodes = []
|
||||
node_ips = {} # node_name -> ip_address
|
||||
|
||||
try:
|
||||
# Получаем список нод
|
||||
nodes_response = await _run_in_executor(
|
||||
http_requests.get,
|
||||
f"{base_url}/api2/json/nodes",
|
||||
headers=headers,
|
||||
cookies={"PVEAuthCookie": ticket},
|
||||
verify=verify_ssl,
|
||||
timeout=15
|
||||
)
|
||||
if nodes_response.status_code == 200:
|
||||
nodes_data = nodes_response.json().get("data", [])
|
||||
cluster_nodes = [n for n in nodes_data if n.get("node")]
|
||||
logger.info(f"Found {len(cluster_nodes)} nodes in cluster")
|
||||
|
||||
# Получаем IP адреса нод из cluster/status
|
||||
if len(cluster_nodes) > 1:
|
||||
cluster_status_response = await _run_in_executor(
|
||||
http_requests.get,
|
||||
f"{base_url}/api2/json/cluster/status",
|
||||
headers=headers,
|
||||
cookies={"PVEAuthCookie": ticket},
|
||||
verify=verify_ssl,
|
||||
timeout=15
|
||||
)
|
||||
if cluster_status_response.status_code == 200:
|
||||
cluster_status = cluster_status_response.json().get("data", [])
|
||||
for item in cluster_status:
|
||||
if item.get("type") == "node" and item.get("name") and item.get("ip"):
|
||||
node_ips[item.get("name")] = item.get("ip")
|
||||
logger.info(f"Node {item.get('name')} IP: {item.get('ip')}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not get cluster nodes: {e}")
|
||||
|
||||
# 4. Сохранить серверы в базу
|
||||
created_servers = []
|
||||
|
||||
if len(cluster_nodes) > 1:
|
||||
# Это кластер - добавляем все ноды
|
||||
for node_info in cluster_nodes:
|
||||
node_name = node_info.get("node")
|
||||
# Используем IP ноды из cluster/status, или IP через который подключились
|
||||
node_ip = node_ips.get(node_name, ip_address)
|
||||
|
||||
# Имя сервера: "BaseName - NodeName" или просто NodeName
|
||||
if name:
|
||||
server_name = f"{name} - {node_name}"
|
||||
else:
|
||||
server_name = node_name
|
||||
|
||||
# Проверяем дубликаты
|
||||
existing = db.query(ProxmoxServer).filter(ProxmoxServer.name == server_name).first()
|
||||
if existing:
|
||||
logger.info(f"Server '{server_name}' already exists, skipping")
|
||||
continue
|
||||
|
||||
# Для каждой ноды создаём токен (используем тот же, так как токен кластерный)
|
||||
server = ProxmoxServer(
|
||||
name=server_name,
|
||||
hostname=node_name,
|
||||
ip_address=node_ip, # Используем реальный IP ноды
|
||||
port=port,
|
||||
api_user=api_user,
|
||||
api_token_name=token_name,
|
||||
api_token_value=token_value,
|
||||
use_password=token_value is None,
|
||||
password=password,
|
||||
verify_ssl=verify_ssl,
|
||||
description=f"{description or ''} (Node: {node_name})".strip(),
|
||||
is_online=node_info.get("status") == "online"
|
||||
)
|
||||
|
||||
db.add(server)
|
||||
created_servers.append({
|
||||
"name": server_name,
|
||||
"node": node_name,
|
||||
"ip": node_ip,
|
||||
"status": node_info.get("status", "unknown")
|
||||
})
|
||||
|
||||
db.commit()
|
||||
logger.info(f"User {current_user.username} auto-setup Proxmox cluster with {len(created_servers)} nodes")
|
||||
|
||||
# Мгновенный sync VM-кэша + WS-broadcast для real-time обновления UI
|
||||
try:
|
||||
from ...workers.monitoring_worker import monitoring_worker
|
||||
monitoring_worker.sync_vm_cache()
|
||||
except Exception as _e:
|
||||
logger.debug(f"VM sync after auto-setup failed: {_e}")
|
||||
try:
|
||||
from ...websocket_manager import broadcast_event
|
||||
broadcast_event("server_added", cluster=True, count=len(created_servers))
|
||||
except Exception as _e:
|
||||
logger.debug(f"Failed to broadcast server_added (cluster): {_e}")
|
||||
|
||||
return {
|
||||
"cluster": True,
|
||||
"nodes_count": len(created_servers),
|
||||
"servers": created_servers,
|
||||
"message": f"Добавлено {len(created_servers)} нод кластера"
|
||||
}
|
||||
else:
|
||||
# Одиночный сервер
|
||||
server = ProxmoxServer(
|
||||
name=name,
|
||||
hostname=hostname or ip_address,
|
||||
ip_address=ip_address,
|
||||
port=port,
|
||||
api_user=api_user,
|
||||
api_token_name=token_name,
|
||||
api_token_value=token_value,
|
||||
use_password=token_value is None,
|
||||
password=password,
|
||||
verify_ssl=verify_ssl,
|
||||
description=description,
|
||||
is_online=True
|
||||
)
|
||||
|
||||
db.add(server)
|
||||
db.commit()
|
||||
db.refresh(server)
|
||||
|
||||
logger.info(f"User {current_user.username} auto-setup Proxmox server: {server.name}")
|
||||
|
||||
# Мгновенный sync VM-кэша + WS-broadcast для real-time обновления UI
|
||||
try:
|
||||
from ...workers.monitoring_worker import monitoring_worker
|
||||
monitoring_worker.sync_vm_cache()
|
||||
except Exception as _e:
|
||||
logger.debug(f"VM sync after auto-setup failed: {_e}")
|
||||
try:
|
||||
from ...websocket_manager import broadcast_event
|
||||
broadcast_event(
|
||||
"server_added",
|
||||
server_id=server.id,
|
||||
name=server.name,
|
||||
is_online=bool(server.is_online),
|
||||
)
|
||||
except Exception as _e:
|
||||
logger.debug(f"Failed to broadcast server_added: {_e}")
|
||||
|
||||
return {
|
||||
"cluster": False,
|
||||
"id": server.id,
|
||||
"name": server.name,
|
||||
"hostname": server.hostname,
|
||||
"ip_address": server.ip_address,
|
||||
"port": server.port,
|
||||
"api_user": server.api_user,
|
||||
"api_token_name": token_name or "(пароль)",
|
||||
"use_password": server.use_password,
|
||||
"verify_ssl": server.verify_ssl,
|
||||
"description": server.description,
|
||||
"is_online": server.is_online
|
||||
}
|
||||
|
||||
|
||||
@router.get("/api/servers/{server_id}", response_model=ProxmoxServerResponse)
|
||||
|
||||
Reference in New Issue
Block a user