Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit 697f890

Browse files
committed
resize safety constraints
1 parent b70b638 commit 697f890

1 file changed

Lines changed: 33 additions & 14 deletions

File tree

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu

packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
import os
66
import platform
7+
import shutil
78
from collections.abc import AsyncGenerator
89
from dataclasses import dataclass, field
910
from functools import cached_property
@@ -20,7 +21,7 @@
2021
from jumpstarter_driver_opendal.driver import FlasherInterface
2122
from jumpstarter_driver_power.driver import PowerInterface, PowerReading
2223
from jumpstarter_driver_pyserial.driver import PySerial
23-
from pydantic import BaseModel, Field, validate_call
24+
from pydantic import BaseModel, ByteSize, Field, TypeAdapter, validate_call
2425
from qemu.qmp import QMPClient
2526
from qemu.qmp.protocol import ConnectError, Runstate
2627

@@ -153,19 +154,6 @@ async def on(self) -> None: # noqa: C901
153154
]
154155

155156
if root.exists():
156-
# Resize disk if configured
157-
if self.parent.disk_size:
158-
self.logger.info(f"Resizing disk to {self.parent.disk_size}")
159-
resize_proc = await run_process(
160-
["qemu-img", "resize", str(root), self.parent.disk_size],
161-
stdout=PIPE,
162-
stderr=PIPE,
163-
)
164-
try:
165-
resize_proc.check_returncode()
166-
except CalledProcessError as e:
167-
raise RuntimeError(f"Failed to resize disk: {resize_proc.stderr.decode()}") from e
168-
169157
proc = await run_process(
170158
["qemu-img", "info", "--output=json", str(root)],
171159
stdout=PIPE,
@@ -175,6 +163,7 @@ async def on(self) -> None: # noqa: C901
175163
proc.check_returncode()
176164
info = json.loads(proc.stdout)
177165
image_format = info.get("format", "raw")
166+
current_virtual_size = info.get("virtual-size") or root.stat().st_size
178167
match image_format:
179168
case "raw" | "qcow2" | "qcow" | "vmdk":
180169
image_driver = image_format
@@ -183,6 +172,36 @@ async def on(self) -> None: # noqa: C901
183172
except CalledProcessError:
184173
self.logger.warning("unable to detect image format, assuming raw")
185174
image_driver = "raw"
175+
current_virtual_size = root.stat().st_size
176+
177+
# Resize disk if configured
178+
if self.parent.disk_size:
179+
# Convert QEMU binary format (20G) to Pydantic format (20GiB)
180+
s = self.parent.disk_size
181+
requested = int(TypeAdapter(ByteSize).validate_python(s + "iB" if s[-1] in "kmgtKMGT" else s))
182+
183+
if requested < current_virtual_size:
184+
raise RuntimeError(
185+
f"Shrinking disk is not supported: current {ByteSize(current_virtual_size).human_readable()}, "
186+
f"requested {self.parent.disk_size}"
187+
)
188+
189+
available = shutil.disk_usage(root.parent).free
190+
if requested > available:
191+
raise RuntimeError(
192+
f"Not enough disk space: need {ByteSize(requested).human_readable()}, "
193+
f"only {ByteSize(available).human_readable()} available"
194+
)
195+
196+
if requested > current_virtual_size:
197+
self.logger.info(f"Resizing disk to {ByteSize(requested).human_readable()}")
198+
proc = await run_process(
199+
["qemu-img", "resize", str(root), str(requested)],
200+
stdout=PIPE,
201+
stderr=PIPE,
202+
)
203+
if proc.returncode != 0:
204+
raise RuntimeError(f"Failed to resize disk: {proc.stderr.decode()}")
186205

187206
cmdline += [
188207
"-blockdev",

0 commit comments

Comments
 (0)