diff --git a/backend/app/proxmox_client.py b/backend/app/proxmox_client.py index a9b87f7..42b4b50 100644 --- a/backend/app/proxmox_client.py +++ b/backend/app/proxmox_client.py @@ -47,27 +47,6 @@ def clone_vm(source_vmid: int, new_vmid: int, name: str, node: str = None, stora _wait_task(px, node, upid) -def clone_lxc(source_vmid: int, new_vmid: int, name: str, cores: int, memory_mb: int, password: str, node: str = None) -> str: - """Клонирует LXC-шаблон по VMID и задаёт ресурсы и пароль.""" - node = node or settings.pve_node - px = _client() - upid = px.nodes(node).lxc(source_vmid).clone.post( - newid=new_vmid, - hostname=name, - full=1, - ) - _wait_task(px, node, upid) - px.nodes(node).lxc(new_vmid).config.put( - hostname=name, - cores=cores, - memory=memory_mb, - swap=memory_mb, - ) - # У LXC отдельный endpoint для смены пароля root. - px.nodes(node).lxc(new_vmid).passwd.post(password=password) - return password - - def resize_vm(vmid: int, cores: int, memory_mb: int, node: str = None) -> None: """Изменяет количество vCPU и RAM у VM.""" node = node or settings.pve_node @@ -84,10 +63,23 @@ def create_lxc( storage: str = "local-lvm", node: str = None, ) -> str: - """Создаёт LXC из архива vzdump-шаблона.""" + """Создаёт LXC из архивного шаблона и запускает его. + + Шаги (каждый — отдельный запрос к Proxmox API): + 1. Создаёт LXC через ``ostemplate`` (без запуска). + 2. Задаёт новый root-пароль через ``/passwd``. + 3. Создаёт rootfs указанного размера через ``/resize``. + 4. Запускает контейнер через ``/status/start``. + + LXC создаётся ТОЛЬКО из архивного шаблона (vztmpl на storage). + Клонирование существующего LXC по VMID не поддерживается. + """ node = node or settings.pve_node px = _client() password = gen_password() + + # 1. Создаём LXC через ostemplate. start=0 — не запускаем, чтобы успеть + # задать пароль и диск нужного размера до старта. upid = px.nodes(node).lxc.post( vmid=new_vmid, hostname=name, @@ -95,12 +87,29 @@ def create_lxc( cores=cores, memory=memory_mb, swap=memory_mb, - rootfs=f"{storage}:{disk_gb}", - password=password, net0="name=eth0,bridge=vmbr0,ip=dhcp", unprivileged=1, + password=password, + start=0, ) _wait_task(px, node, upid) + + # 2. Задаём новый root-пароль через отдельный endpoint /passwd. + # Поле password в POST /lxc на некоторых версиях Proxmox игнорируется, + # поэтому дублируем через /passwd — это идемпотентная операция. + px.nodes(node).lxc(new_vmid).passwd.post(password=password) + + # 3. Создаём rootfs указанного размера (storage:size_G). + px.nodes(node).lxc(new_vmid).resize.put( + disk="rootfs", + size=f"{storage}:{disk_gb}", + ) + + # 4. Запускаем контейнер. + upid = px.nodes(node).lxc(new_vmid).status.start.post() + if upid: + _wait_task(px, node, upid) + return password @@ -151,7 +160,7 @@ def list_vm_templates(node: str = None) -> list: def list_lxc_templates(node: str = None) -> list: - """Возвращает архивные LXC-шаблоны из доступных хранилищ.""" + """Возвращает архивные LXC-шаблоны (vztmpl) из доступных хранилищ.""" node = node or settings.pve_node px = _client() result = [] @@ -251,7 +260,6 @@ def get_live_stats(vmid: int, node: str = None) -> dict: return {"cpu": 0, "mem_used": 0, "mem_total": 0, "uptime": 0, "status": "unknown"} -get_live_stats_lxc = get_live_stats _VALID_ACTIONS = {"start", "stop", "shutdown", "reboot"}