243 lines
9.9 KiB
Python
243 lines
9.9 KiB
Python
"""Tests for the Proxmox compute client."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import pytest
|
|
import respx
|
|
from httpx import Response
|
|
|
|
from conduit_client import AsyncConduitClient, ConduitClient
|
|
from conduit_client.models.compute import (
|
|
CloneVmRequest,
|
|
ComputeTicketRequest,
|
|
CreateContainerRequest,
|
|
CreateSnapshotRequest,
|
|
CreateVmRequest,
|
|
MigrateVmRequest,
|
|
NodeTasksQuery,
|
|
StorageQuery,
|
|
UploadStorageRequest,
|
|
)
|
|
|
|
|
|
@respx.mock
|
|
def test_auth_ticket(api_key: str) -> None:
|
|
route = respx.post("https://api.example.com/compute/auth/ticket").mock(
|
|
return_value=Response(
|
|
200,
|
|
json={
|
|
"ticket": "tck",
|
|
"CSRFPreventionToken": "csrf",
|
|
"username": "root",
|
|
},
|
|
)
|
|
)
|
|
with ConduitClient(api_key, base_url="https://api.example.com") as client:
|
|
result = client.compute.auth_ticket(
|
|
ComputeTicketRequest(username="root", password="pw")
|
|
)
|
|
assert result.ticket == "tck"
|
|
assert result.csrfpreventiontoken == "csrf"
|
|
assert json.loads(route.calls[0].request.content) == {
|
|
"username": "root",
|
|
"password": "pw",
|
|
}
|
|
|
|
|
|
@respx.mock
|
|
def test_list_resources_and_nodes(api_key: str) -> None:
|
|
respx.get("https://api.example.com/compute/resources").mock(
|
|
return_value=Response(200, json=[{"id": "qemu/100"}])
|
|
)
|
|
respx.get("https://api.example.com/compute/resources").mock(
|
|
return_value=Response(200, json=[{"id": "qemu/100"}])
|
|
)
|
|
respx.get("https://api.example.com/compute/cluster/status").mock(
|
|
return_value=Response(200, json={"quorate": True})
|
|
)
|
|
respx.get("https://api.example.com/compute/nodes").mock(
|
|
return_value=Response(200, json=[{"node": "pve"}])
|
|
)
|
|
with ConduitClient(api_key, base_url="https://api.example.com") as client:
|
|
assert client.compute.list_resources() == [{"id": "qemu/100"}]
|
|
assert client.compute.list_resources(type_="vm") == [{"id": "qemu/100"}]
|
|
assert client.compute.cluster_status() == {"quorate": True}
|
|
assert client.compute.list_nodes() == [{"node": "pve"}]
|
|
|
|
|
|
@respx.mock
|
|
def test_node_status_and_tasks(api_key: str) -> None:
|
|
respx.get("https://api.example.com/compute/nodes/pve/status").mock(
|
|
return_value=Response(200, json={"cpu": 0.1})
|
|
)
|
|
route = respx.get("https://api.example.com/compute/nodes/pve/tasks").mock(
|
|
return_value=Response(200, json=[])
|
|
)
|
|
with ConduitClient(api_key, base_url="https://api.example.com") as client:
|
|
assert client.compute.get_node_status("pve") == {"cpu": 0.1}
|
|
assert client.compute.list_node_tasks("pve", NodeTasksQuery(limit=10)) == []
|
|
assert route.calls[0].request.url.params["limit"] == "10"
|
|
|
|
|
|
@respx.mock
|
|
def test_vm_lifecycle(api_key: str) -> None:
|
|
respx.get("https://api.example.com/compute/vms").mock(
|
|
return_value=Response(200, json=[{"vmid": 100}])
|
|
)
|
|
create_route = respx.post("https://api.example.com/compute/vms").mock(
|
|
return_value=Response(200, json={"task": "UPID:abc"})
|
|
)
|
|
respx.get("https://api.example.com/compute/vms/100").mock(
|
|
return_value=Response(200, json={"vmid": 100, "name": "vm1"})
|
|
)
|
|
patch_route = respx.patch("https://api.example.com/compute/vms/100").mock(
|
|
return_value=Response(200, json={"task": "UPID:patch"})
|
|
)
|
|
respx.delete("https://api.example.com/compute/vms/100").mock(
|
|
return_value=Response(204)
|
|
)
|
|
respx.post("https://api.example.com/compute/vms/100/start").mock(
|
|
return_value=Response(200, json={"task": "UPID:start"})
|
|
)
|
|
respx.post("https://api.example.com/compute/vms/100/stop").mock(
|
|
return_value=Response(200, json={"task": "UPID:stop"})
|
|
)
|
|
respx.post("https://api.example.com/compute/vms/100/shutdown").mock(
|
|
return_value=Response(200, json={"task": "UPID:shutdown"})
|
|
)
|
|
respx.post("https://api.example.com/compute/vms/100/reboot").mock(
|
|
return_value=Response(200, json={"task": "UPID:reboot"})
|
|
)
|
|
with ConduitClient(api_key, base_url="https://api.example.com") as client:
|
|
assert client.compute.list_vms() == [{"vmid": 100}]
|
|
result = client.compute.create_vm(
|
|
CreateVmRequest(node="pve", vmid=100, cores=2, memory=1024)
|
|
)
|
|
assert result.task == "UPID:abc"
|
|
assert client.compute.get_vm(100)["name"] == "vm1"
|
|
assert client.compute.patch_vm(100, {"memory": 2048}).task == "UPID:patch"
|
|
client.compute.delete_vm(100, confirm=100)
|
|
assert client.compute.start_vm(100).task == "UPID:start"
|
|
assert client.compute.stop_vm(100).task == "UPID:stop"
|
|
assert client.compute.shutdown_vm(100).task == "UPID:shutdown"
|
|
assert client.compute.reboot_vm(100).task == "UPID:reboot"
|
|
body = json.loads(create_route.calls[0].request.content)
|
|
assert body["node"] == "pve"
|
|
assert body["cores"] == 2
|
|
assert json.loads(patch_route.calls[0].request.content) == {"memory": 2048}
|
|
|
|
|
|
@respx.mock
|
|
def test_vm_clone_migrate_snapshots_agent(api_key: str) -> None:
|
|
respx.post("https://api.example.com/compute/vms/100/clone").mock(
|
|
return_value=Response(200, json={"task": "UPID:clone"})
|
|
)
|
|
respx.post("https://api.example.com/compute/vms/100/migrate").mock(
|
|
return_value=Response(200, json={"task": "UPID:migrate"})
|
|
)
|
|
respx.get("https://api.example.com/compute/vms/100/snapshots").mock(
|
|
return_value=Response(200, json=[{"name": "snap1"}])
|
|
)
|
|
respx.post("https://api.example.com/compute/vms/100/snapshots").mock(
|
|
return_value=Response(200, json={"task": "UPID:snap"})
|
|
)
|
|
respx.post("https://api.example.com/compute/vms/100/agent/exec").mock(
|
|
return_value=Response(200, json={"out-data": "ok"})
|
|
)
|
|
with ConduitClient(api_key, base_url="https://api.example.com") as client:
|
|
assert client.compute.clone_vm(100, CloneVmRequest(newid=200)).task == "UPID:clone"
|
|
assert client.compute.migrate_vm(
|
|
100, MigrateVmRequest(target="pve2", online=True)
|
|
).task == "UPID:migrate"
|
|
assert client.compute.list_snapshots(100) == [{"name": "snap1"}]
|
|
assert client.compute.create_snapshot(
|
|
100, CreateSnapshotRequest(snapname="snap2")
|
|
).task == "UPID:snap"
|
|
assert client.compute.agent_action(100, "exec", {"command": "ls"}) == {"out-data": "ok"}
|
|
|
|
|
|
@respx.mock
|
|
def test_containers(api_key: str) -> None:
|
|
create_route = respx.post("https://api.example.com/compute/containers").mock(
|
|
return_value=Response(200, json={"task": "UPID:ct"})
|
|
)
|
|
respx.post("https://api.example.com/compute/containers/200/start").mock(
|
|
return_value=Response(200, json={"task": "UPID:start"})
|
|
)
|
|
respx.post("https://api.example.com/compute/containers/200/stop").mock(
|
|
return_value=Response(200, json={"task": "UPID:stop"})
|
|
)
|
|
with ConduitClient(api_key, base_url="https://api.example.com") as client:
|
|
result = client.compute.create_container(
|
|
CreateContainerRequest(node="pve", ostemplate="local:vztmpl/debian.tar.gz", vmid=200)
|
|
)
|
|
assert result.task == "UPID:ct"
|
|
assert client.compute.start_container(200).task == "UPID:start"
|
|
assert client.compute.stop_container(200).task == "UPID:stop"
|
|
body = json.loads(create_route.calls[0].request.content)
|
|
assert body["node"] == "pve"
|
|
assert body["ostemplate"] == "local:vztmpl/debian.tar.gz"
|
|
|
|
|
|
@respx.mock
|
|
def test_storage(api_key: str) -> None:
|
|
respx.get("https://api.example.com/compute/storage").mock(
|
|
return_value=Response(200, json=[{"storage": "local"}])
|
|
)
|
|
respx.get("https://api.example.com/compute/storage/local/content").mock(
|
|
return_value=Response(200, json=[{"volid": "local:iso/img.iso"}])
|
|
)
|
|
upload_route = respx.post("https://api.example.com/compute/storage/local/upload").mock(
|
|
return_value=Response(200, json={"task": "UPID:upload"})
|
|
)
|
|
with ConduitClient(api_key, base_url="https://api.example.com") as client:
|
|
assert client.compute.list_storage(StorageQuery(node="pve")) == [{"storage": "local"}]
|
|
assert client.compute.list_storage_content("local", StorageQuery(node="pve")) == [
|
|
{"volid": "local:iso/img.iso"}
|
|
]
|
|
result = client.compute.upload_to_storage(
|
|
"local",
|
|
UploadStorageRequest(content="abc", filename="img.iso"),
|
|
node="pve",
|
|
)
|
|
assert result.task == "UPID:upload"
|
|
assert upload_route.calls[0].request.url.params["node"] == "pve"
|
|
|
|
|
|
@respx.mock
|
|
def test_backup_jobs_and_misc(api_key: str) -> None:
|
|
respx.get("https://api.example.com/compute/backups/jobs").mock(
|
|
return_value=Response(200, json=[])
|
|
)
|
|
respx.post("https://api.example.com/compute/backups/jobs").mock(
|
|
return_value=Response(200, json={"task": "UPID:backup"})
|
|
)
|
|
respx.get("https://api.example.com/compute/ha/resources").mock(
|
|
return_value=Response(200, json=[])
|
|
)
|
|
respx.get("https://api.example.com/compute/access/users").mock(
|
|
return_value=Response(200, json=[])
|
|
)
|
|
respx.get("https://api.example.com/compute/access/acl").mock(
|
|
return_value=Response(200, json=[])
|
|
)
|
|
with ConduitClient(api_key, base_url="https://api.example.com") as client:
|
|
assert client.compute.list_backup_jobs() == []
|
|
assert client.compute.create_backup_job({"id": "job1"}).task == "UPID:backup"
|
|
assert client.compute.list_ha_resources() == []
|
|
assert client.compute.list_access_users() == []
|
|
assert client.compute.list_access_acl() == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_async_start_vm(api_key: str) -> None:
|
|
respx.post("https://api.example.com/compute/vms/100/start").mock(
|
|
return_value=Response(200, json={"task": "UPID:start"})
|
|
)
|
|
async with AsyncConduitClient(api_key, base_url="https://api.example.com") as client:
|
|
result = await client.compute.start_vm(100)
|
|
assert result.task == "UPID:start"
|