Archived
Добавлен Proxmox client для VM и LXC
This commit is contained in:
@@ -0,0 +1,109 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from proxmoxer import ProxmoxAPI
|
||||||
|
|
||||||
|
from .config import settings
|
||||||
|
|
||||||
|
|
||||||
|
def _client() -> ProxmoxAPI:
|
||||||
|
"""Создаёт Proxmox API client по отдельному токену панели."""
|
||||||
|
parsed = urlparse(settings.pve_host)
|
||||||
|
if not parsed.hostname:
|
||||||
|
raise RuntimeError("PVE_HOST имеет некорректный формат")
|
||||||
|
if "!" not in settings.pve_token_name:
|
||||||
|
raise RuntimeError("PVE_TOKEN_NAME должен иметь формат user@realm!token")
|
||||||
|
user, token_name = settings.pve_token_name.split("!", 1)
|
||||||
|
return ProxmoxAPI(
|
||||||
|
parsed.hostname,
|
||||||
|
port=parsed.port or 8006,
|
||||||
|
user=user,
|
||||||
|
token_name=token_name,
|
||||||
|
token_value=settings.pve_token_value.get_secret_value(),
|
||||||
|
verify_ssl=settings.pve_verify_ssl,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_task(client: ProxmoxAPI, node: str, upid: str, timeout: int = 1800) -> None:
|
||||||
|
"""Ожидает завершения задачи Proxmox с ограничением времени."""
|
||||||
|
deadline = time.monotonic() + timeout
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
task = client.nodes(node).tasks(upid).status.get()
|
||||||
|
if task.get("status") == "stopped":
|
||||||
|
if task.get("exitstatus") != "OK":
|
||||||
|
raise RuntimeError("Задача Proxmox завершилась с ошибкой")
|
||||||
|
return
|
||||||
|
time.sleep(2)
|
||||||
|
raise TimeoutError("Превышено время ожидания задачи Proxmox")
|
||||||
|
|
||||||
|
|
||||||
|
def next_vmid() -> int:
|
||||||
|
"""Запрашивает следующий свободный VMID у Proxmox."""
|
||||||
|
return int(_client().cluster.nextid.get())
|
||||||
|
|
||||||
|
|
||||||
|
def provision_vm(
|
||||||
|
source_vmid: int,
|
||||||
|
vmid: int,
|
||||||
|
name: str,
|
||||||
|
cores: int,
|
||||||
|
memory_mb: int,
|
||||||
|
disk_gb: int,
|
||||||
|
username: str,
|
||||||
|
password: str,
|
||||||
|
) -> None:
|
||||||
|
"""Клонирует cloud-init VM, настраивает ресурсы и запускает её."""
|
||||||
|
client = _client()
|
||||||
|
node = settings.pve_node
|
||||||
|
params = {"newid": vmid, "name": name, "full": 1}
|
||||||
|
if settings.pve_vm_storage:
|
||||||
|
params["storage"] = settings.pve_vm_storage
|
||||||
|
upid = client.nodes(node).qemu(source_vmid).clone.post(**params)
|
||||||
|
_wait_task(client, node, upid)
|
||||||
|
client.nodes(node).qemu(vmid).config.put(
|
||||||
|
cores=cores,
|
||||||
|
memory=memory_mb,
|
||||||
|
ciuser=username,
|
||||||
|
cipassword=password,
|
||||||
|
ipconfig0="ip=dhcp",
|
||||||
|
)
|
||||||
|
# Увеличиваем диск только если шаблон меньше заданного размера.
|
||||||
|
config = client.nodes(node).qemu(vmid).config.get()
|
||||||
|
for key in ("scsi0", "virtio0", "sata0", "ide0"):
|
||||||
|
value = config.get(key, "")
|
||||||
|
match = re.search(r"size=(\d+)G", value)
|
||||||
|
if match and disk_gb > int(match.group(1)):
|
||||||
|
client.nodes(node).qemu(vmid).resize.put(
|
||||||
|
disk=key, size=f"+{disk_gb - int(match.group(1))}G"
|
||||||
|
)
|
||||||
|
break
|
||||||
|
client.nodes(node).qemu(vmid).status.start.post()
|
||||||
|
|
||||||
|
|
||||||
|
def provision_lxc(
|
||||||
|
source_template: str,
|
||||||
|
vmid: int,
|
||||||
|
name: str,
|
||||||
|
cores: int,
|
||||||
|
memory_mb: int,
|
||||||
|
disk_gb: int,
|
||||||
|
password: str,
|
||||||
|
) -> None:
|
||||||
|
"""Создаёт LXC из CT-шаблона и запускает его с DHCP."""
|
||||||
|
client = _client()
|
||||||
|
node = settings.pve_node
|
||||||
|
upid = client.nodes(node).lxc.post(
|
||||||
|
vmid=vmid,
|
||||||
|
hostname=name,
|
||||||
|
ostemplate=source_template,
|
||||||
|
cores=cores,
|
||||||
|
memory=memory_mb,
|
||||||
|
swap=memory_mb,
|
||||||
|
rootfs=f"{settings.pve_lxc_storage}:{disk_gb}",
|
||||||
|
password=password,
|
||||||
|
net0=f"name=eth0,bridge={settings.pve_bridge},ip=dhcp",
|
||||||
|
unprivileged=1,
|
||||||
|
)
|
||||||
|
_wait_task(client, node, upid)
|
||||||
|
client.nodes(node).lxc(vmid).status.start.post()
|
||||||
Reference in New Issue
Block a user