feat: implement QEMU disk resize and move
This commit is contained in:
@@ -23,6 +23,7 @@ async def client_for(tmp_path: Path) -> AsyncClient:
|
||||
Parameter(name="node", definition=Schema(type="string")),
|
||||
Parameter(name="count", definition=Schema(type="integer", minimum=1)),
|
||||
Parameter(name="force", definition=Schema(type="boolean", optional=True)),
|
||||
Parameter(name="scsi[n]", definition=Schema(type="string", optional=True)),
|
||||
),
|
||||
returns=Schema(type="null"),
|
||||
checksum="1" * 64,
|
||||
@@ -41,6 +42,8 @@ async def client_for(tmp_path: Path) -> AsyncClient:
|
||||
|
||||
async def handler(_request: Request, inputs: dict[str, Any]) -> None:
|
||||
assert inputs["values"]["count"] >= 1
|
||||
if "scsi0" in inputs["values"]:
|
||||
assert inputs["values"]["scsi0"] == "local:disk,size=8G"
|
||||
return None
|
||||
|
||||
handlers.register("/nodes/{node}/test", "POST", handler)
|
||||
@@ -96,3 +99,12 @@ async def test_non_object_json_is_rejected_without_fastapi_body(tmp_path: Path)
|
||||
assert response.status_code == 400
|
||||
assert response.json()["errors"] == {"body": "expected an object"}
|
||||
assert "detail" not in response.json()
|
||||
|
||||
|
||||
async def test_indexed_contract_parameter_accepts_concrete_device(tmp_path: Path) -> None:
|
||||
async with await client_for(tmp_path) as client:
|
||||
response = await client.post(
|
||||
"/api2/json/nodes/pve/test", json={"count": 1, "scsi0": "local:disk,size=8G"}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
@@ -66,7 +66,11 @@ class QemuPool:
|
||||
return {"state": '{"status":"stopped"}', "config": '{"name":"vm"}'}
|
||||
if "SELECT r.id, r.state" in sql:
|
||||
status = "running" if self.running else "stopped"
|
||||
return {"id": self.resource_id, "state": f'{{"status":"{status}"}}'}
|
||||
return {
|
||||
"id": self.resource_id,
|
||||
"state": f'{{"status":"{status}"}}',
|
||||
"config": '{"scsi0":"local-lvm:vm-150-disk-0,size=8G"}',
|
||||
}
|
||||
if "SELECT r.id, r.state, v.config" in sql:
|
||||
return {"id": self.resource_id, "state": '{"status":"stopped"}', "config": "{}"}
|
||||
if "SELECT s.* FROM snapshots" in sql:
|
||||
@@ -292,7 +296,9 @@ async def test_qemu_clone_and_migrate_handlers(monkeypatch: pytest.MonkeyPatch)
|
||||
clone = registry.get("/nodes/{node}/qemu/{vmid}/clone", "POST")
|
||||
migrate_get = registry.get("/nodes/{node}/qemu/{vmid}/migrate", "GET")
|
||||
migrate = registry.get("/nodes/{node}/qemu/{vmid}/migrate", "POST")
|
||||
assert clone and migrate_get and migrate
|
||||
resize = registry.get("/nodes/{node}/qemu/{vmid}/resize", "PUT")
|
||||
move = registry.get("/nodes/{node}/qemu/{vmid}/move_disk", "POST")
|
||||
assert clone and migrate_get and migrate and resize and move
|
||||
|
||||
clone_upid = await clone(
|
||||
http_request, inputs(node="pve1", vmid=150, newid=151, name="clone", full=True)
|
||||
@@ -307,6 +313,14 @@ async def test_qemu_clone_and_migrate_handlers(monkeypatch: pytest.MonkeyPatch)
|
||||
)
|
||||
assert migrate_upid.startswith("UPID:pve1:")
|
||||
assert tasks[-1]["task_type"] == "qemu-migrate"
|
||||
assert (
|
||||
await resize(http_request, inputs(node="pve1", vmid=150, disk="scsi0", size="+2G")) is None
|
||||
)
|
||||
move_upid = await move(
|
||||
http_request, inputs(node="pve1", vmid=150, disk="scsi0", storage="local")
|
||||
)
|
||||
assert move_upid.startswith("UPID:pve1:")
|
||||
assert tasks[-1]["task_type"] == "qemu-move-disk"
|
||||
|
||||
with pytest.raises(ApiError) as same_node:
|
||||
await migrate(http_request, inputs(node="pve1", vmid=150, target="pve1"))
|
||||
|
||||
@@ -84,6 +84,8 @@ class CrudConnection:
|
||||
return {"state": '{"status":"stopped","name":"old"}', "config": '{"name":"old"}'}
|
||||
if "SELECT state FROM resources" in sql:
|
||||
return {"state": '{"status":"stopped","name":"old"}'}
|
||||
if "SELECT config FROM virtual_machines" in sql:
|
||||
return {"config": '{"scsi0":"local-lvm:vm-150-disk-0,size=10G"}'}
|
||||
if "FROM snapshots" in sql:
|
||||
return {
|
||||
"state": (
|
||||
@@ -255,9 +257,28 @@ async def test_qemu_worker_clone_and_migrate_are_persistent() -> None:
|
||||
1,
|
||||
)
|
||||
)
|
||||
moved = await handler(
|
||||
Task(
|
||||
uuid.uuid4(),
|
||||
"UPID:move",
|
||||
"qemu-move-disk",
|
||||
"running",
|
||||
{
|
||||
"resource_id": str(resource_id),
|
||||
"disk": "scsi0",
|
||||
"target_disk": "scsi0",
|
||||
"storage": "local",
|
||||
"delete": True,
|
||||
},
|
||||
0,
|
||||
False,
|
||||
1,
|
||||
)
|
||||
)
|
||||
|
||||
assert cloned == {"vmid": 151, "node": "pve1"}
|
||||
assert migrated == {"node": "pve2", "status": "stopped"}
|
||||
assert moved == {"disk": "scsi0", "storage": "local"}
|
||||
commands = repository.connection.commands
|
||||
assert any("INSERT INTO resources" in command for command in commands)
|
||||
assert any("node_id=$2" in command for command in commands)
|
||||
|
||||
Reference in New Issue
Block a user