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

Add disk resize support for QEMU driver#793

Merged
bennyz merged 3 commits intojumpstarter-dev:mainfrom
evakhoni:qemu_resize
Jan 8, 2026
Merged

Add disk resize support for QEMU driver#793
bennyz merged 3 commits intojumpstarter-dev:mainfrom
evakhoni:qemu_resize

Conversation

@evakhoni
Copy link
Contributor

@evakhoni evakhoni commented Jan 6, 2026

closing https://github.com/jumpstarter-dev/jumpstarter/issues/792

Adds the ability to resize QEMU disk images before boot. This enables users to expand disk images to a larger size without manual intervention.

Changes

Driver (driver.py):

  • Added disk_size: str | None config field to Qemu class
  • Added disk resize logic in QemuPower.on() using qemu-img resize before VM boot
  • Added set_disk_size() exported method for runtime override

Client (client.py):

  • Added QemuClient.cli() with new resize command group
  • Added j qemu resize disk <size> command for runtime disk size configuration

Usage

Via exporter config:

export:
  qemu:
    type: jumpstarter_driver_qemu.driver.Qemu
    config:
      disk_size: "20G"

Via CLI (overrides config):

j qemu flasher flash image.qcow2
j qemu resize disk 30G
j power on

Override priority
CLI command > config file

Notes

  • Disk resize uses qemu-img resize, but intentionally limited to absolute (20G) sizes
  • Shrinking is currently disabled for simplicity and to prevent disk image corruption
  • Resize happens before VM boot, after image is flashed
  • Setting persists in memory until the exporter restarts

Bonus
Also added j qemu resize memory command for overriding memory allocation at runtime, following the same pattern.

Summary by CodeRabbit

  • New Features

    • VM disk resizing before boot and memory allocation for the next boot via human-friendly size inputs (e.g., G, M).
    • CLI commands to set disk and memory sizes with confirmation feedback.
  • Bug Fixes / Validation

    • Prevents shrinking existing disks and blocks resizing that would exceed host capacity; surfaces clear errors.
  • Tests

    • Added tests for resize success, insufficient-space blocking, shrink-blocking, and setter validation.

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link

netlify bot commented Jan 6, 2026

Deploy Preview for jumpstarter-docs ready!

Name Link
🔨 Latest commit be4d299
🔍 Latest deploy log https://app.netlify.com/projects/jumpstarter-docs/deploys/695fe8d394ead000088f6aa9
😎 Deploy Preview https://deploy-preview-793--jumpstarter-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

📝 Walkthrough

Walkthrough

Adds disk and memory sizing controls: new QemuClient CLI and setters; a disk_size field, size parsing/validation, and pre-boot qemu-img resize flow in the Qemu driver; and tests covering shrink, insufficient-space, and successful resize scenarios.

Changes

Cohort / File(s) Summary
Client CLI
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
Added Click imports and driver_click_group; added cli() exposing a resize subgroup; added set_disk_size(size: str) and set_memory_size(size: str) on QemuClient which store targets and echo confirmations.
Driver core & boot flow
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
Added `disk_size: str
Tests
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
Added anyio_backend and resize_test fixtures, _mock_qemu_img_info helper, and tests: test_resize_shrink_blocked, test_resize_insufficient_space_blocked, test_resize_succeeds, plus valid/invalid setter tests for disk and memory sizes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as Click CLI
    participant Client as QemuClient
    participant Driver as Qemu Driver
    participant Host as Host FS / qemu-img

    User->>CLI: run "resize disk <size>" or "resize memory <size>"
    CLI->>Client: set_disk_size(size) / set_memory_size(size)
    Client->>Driver: store target size
    CLI-->>User: echo confirmation

    Note over Driver,Host: On next boot/init when disk_size is set
    Driver->>Host: qemu-img info <image>
    Host-->>Driver: virtual_size
    alt target > virtual_size
        Driver->>Host: qemu-img resize <image> <disk_size>
        Host-->>Driver: success / error
        alt error
            Driver->>Driver: raise RuntimeError
        end
    else target <= virtual_size
        Driver->>Driver: skip resize (shrinking blocked or unnecessary)
    end
    Driver->>Driver: launch QEMU with configured memory (if set)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • jumpstarter-dev/jumpstarter#321 — Adds matching Qemu and QemuClient changes (disk/memory setters, CLI, resize logic) that appear directly related to these edits.

Suggested reviewers

  • mangelajo
  • bennyz
  • bkhizgiy

Poem

🐰 I nibbled bytes and stretched the rim,
I tugged at disks and set memory trim.
Clicked a command, set sizes neat,
qemu-img hummed — the resize complete.
Hop, boot, run — a rabbit's tiny feat!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'Add disk resize support for QEMU driver' directly and clearly describes the main change: adding disk resizing functionality to the QEMU driver.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py (1)

45-70: CLI structure looks good!

The nested command group organization is clean and follows Click best practices. Minor consideration: the echo message "Disk will be resized to {size}" could be slightly confusing for relative sizes like "+5G" (which means "grow by 5G" rather than "set to 5G"), but the help text clarifies the accepted formats.

Optional: Slightly clearer message for relative sizes
         @resize.command(name="disk")
         @click.argument("size")
         def resize_disk(size):
             """Resize the root disk (e.g., 20G, +5G). Run before power on."""
             self.set_disk_size(size)
-            click.echo(f"Disk will be resized to {size} on next power on")
+            click.echo(f"Disk size will be set to {size} on next power on")

Or alternatively, keep as-is since qemu-img resize uses similar terminology.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8dd161a and a590c0c.

📒 Files selected for processing (2)
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
🧰 Additional context used
📓 Path-based instructions (4)
packages/jumpstarter-driver-*/jumpstarter_driver_*/driver.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with *Power, Network drivers with *Network, Flasher drivers with *Flasher, Console drivers with *Console, Server drivers with *Server

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
🧠 Learnings (2)
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py : Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use `CompositeClient` from `jumpstarter_driver_composite.client` for composite drivers with child drivers.

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `client.py` file containing the client implementation

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
🧬 Code graph analysis (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py (2)
  • set_disk_size (23-25)
  • set_memory_size (27-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: build
  • GitHub Check: e2e
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (macos-15, 3.11)
🔇 Additional comments (5)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (3)

156-167: LGTM!

The disk resize logic is well-integrated into the boot flow. The resize correctly occurs after flashing (when root.exists()) and before VM startup. Error handling appropriately captures stderr for diagnostics.


263-263: LGTM!

The optional disk_size field with sensible defaults enables opt-in disk resizing. The inline comment documenting the format options is helpful.


383-393: LGTM!

The setter methods follow the established pattern of other exported methods in this class. Using @validate_call ensures type safety, and runtime validation by QEMU tools is an acceptable approach.

packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py (2)

3-7: LGTM!

Imports follow the established pattern for driver CLIs using driver_click_group as per project conventions.


23-29: LGTM!

The client-side setter methods correctly proxy to the driver via RPC, following the established pattern used by property accessors above.

@evakhoni
Copy link
Contributor Author

evakhoni commented Jan 6, 2026

cc @mangelajo @bennyz @bkhizgiy for review. thanks!

@bennyz
Copy link
Member

bennyz commented Jan 6, 2026

do we want to consider the resources available on the host?

@bkhizgiy
Copy link
Contributor

bkhizgiy commented Jan 6, 2026

overall looking good. Adding to @bennyz question, if we try to run the resize cmd and there are not enough resources, will we get some error back?
++ I would consider adding some unit testing to cover this new functionality.

@evakhoni
Copy link
Contributor Author

evakhoni commented Jan 6, 2026

yep unit tests are a good idea.. I was waiting to see what you think about the approach before I implement those...

for the resources, I have this check that is forwarding stderr from qemu-img command. sufficient?

@bennyz
Copy link
Member

bennyz commented Jan 6, 2026

it is thin provisioned so you can request more than what host has, it will fail only when the space is actually used

@evakhoni
Copy link
Contributor Author

evakhoni commented Jan 6, 2026

ah good point.. I need to check that 👍

@evakhoni evakhoni marked this pull request as draft January 6, 2026 12:25
@evakhoni evakhoni marked this pull request as ready for review January 7, 2026 14:36
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:
- Line 289: The comment for disk_size currently claims support for relative
sizes like "+5G" but QemuPower.on() validates/parses only absolute sizes; either
remove the "+5G" example from the disk_size comment to reflect current behavior,
or update QemuPower.on() to detect and preserve a leading '+' or '-' prefix
(e.g., check disk_size[0] in ("+","-"), strip/validate the rest as the absolute
magnitude, then reattach the sign when invoking qemu-img resize) so relative
resizes are passed through unchanged; update the disk_size comment to match the
implemented behavior and adjust any validation logic in QemuPower.on()
accordingly.
- Around line 415-419: The set_memory_size method lacks input validation for the
size string; update set_memory_size to validate the incoming size (reuse the
same logic used by set_disk_size — e.g., call the shared validate_size helper or
the same regex/parse routine) and raise a clear ValueError on invalid formats
before assigning self.mem, so invalid memory specs are caught early; keep the
@validate_call(validate_return=True) decorator and only set self.mem after
successful validation.
- Around line 409-413: The set_disk_size method accepts arbitrary strings but
lacks format validation; update set_disk_size to validate the incoming size
string (e.g., numeric + optional unit like K/M/G/T with or without "B", or
whatever project standard is used) before assigning to self.disk_size and raise
a clear ValueError for invalid formats so callers fail fast; implement the check
inside set_disk_size (or extend the validate_call rule) and keep the assignment
to self.disk_size only after the format check.
- Around line 186-188: The current logic around self.parent.disk_size blindly
appends "iB" and can create invalid strings like "20GBiB"; to fix, normalize the
input before calling TypeAdapter(ByteSize).validate_python by detecting common
suffix patterns (case-insensitive) and only appending the proper unit when
missing: if the string already contains "B" or "iB" (e.g., "GB", "GiB", "MiB")
leave it as-is, if it ends with a single letter unit like "K/M/G/T" append "iB",
and if it has no suffix append a default unit (or reject/raise a clear
validation error); update the code that constructs requested to use this
normalized size string instead of naively doing s + "iB".
🧹 Nitpick comments (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py (1)

147-164: Consider expanding test coverage.

The test validates the happy path effectively, but additional coverage would strengthen the test suite:

  1. Relative size support: The documentation mentions "+5G" format, but no test covers relative sizes.
  2. Log verification: The resize operation logs the action (line 204 in driver.py), but this isn't verified.
  3. Edge case: Test when requested == current_virtual_size (no resize needed).
📝 Additional test cases
@pytest.mark.anyio
async def test_resize_no_op_when_equal():
    """Resize should be skipped if requested size equals current size."""
    driver, current = _setup_resize_test("10G", 10)  # requested: 10G, current: 10G
    
    with patch("jumpstarter_driver_qemu.driver.run_process", side_effect=_mock_qemu_img_info(current)) as mock_run:
        with patch("jumpstarter_driver_qemu.driver.Popen"):
            try:
                await driver.children["power"].on()
            except Exception:
                pass
    
    # Verify resize was NOT called
    resize_calls = [c for c in mock_run.call_args_list if "resize" in c[0][0]]
    assert not resize_calls, "qemu-img resize should not be called when sizes are equal"

@pytest.mark.anyio
async def test_resize_with_relative_size():
    """Test resize with relative size format."""
    driver, current = _setup_resize_test("+5G", 10)  # relative: +5G, current: 10G
    # Implementation would need to handle relative sizes differently
    # This test would currently fail and indicates a gap in the implementation
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a590c0c and b884841.

📒 Files selected for processing (2)
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
🧰 Additional context used
📓 Path-based instructions (3)
packages/jumpstarter-driver-*/jumpstarter_driver_*/driver.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with *Power, Network drivers with *Network, Flasher drivers with *Flasher, Console drivers with *Console, Server drivers with *Server

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
🧠 Learnings (2)
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py : Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: After driver creation, implement driver logic in `driver.py`, add tests in `driver_test.py`, update README.md with specific documentation, and test the driver

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
🧬 Code graph analysis (2)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py (2)
  • set_disk_size (23-25)
  • set_memory_size (27-29)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (1)
  • on (73-250)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (macos-15, 3.11)
  • GitHub Check: e2e
🔇 Additional comments (7)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (3)

7-7: LGTM!

The import additions are appropriate for the disk resize functionality—shutil for disk space checks and ByteSize/TypeAdapter for size parsing and formatting.

Also applies to: 24-24


173-173: LGTM!

The virtual size calculation correctly falls back to file size when qemu-img info doesn't provide virtual-size or fails entirely. This ensures the resize logic has a valid baseline to work with.

Also applies to: 182-183


203-211: The code is correct as written. According to qemu-img documentation, raw byte integers (e.g., 104857600) are the documented standard format for the SIZE argument and are shown as the primary example in official documentation. The current implementation properly passes str(requested) as a raw byte count, which is fully supported. Additionally, the code already logs a human-readable version via ByteSize(requested).human_readable() on line 204, so readability in logs is not a concern.

Likely an incorrect or invalid review comment.

packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py (4)

1-1: LGTM!

The import additions and anyio_backend fixture are appropriate. Forcing the asyncio backend ensures consistent test behavior across environments.

Also applies to: 7-7, 18-21


104-121: LGTM!

The helper functions are well-designed. _setup_resize_test creates the test environment efficiently using sparse files, and _mock_qemu_img_info provides clean mocking for the qemu-img info command.


124-131: LGTM!

This test correctly validates that the resize logic blocks disk shrinking operations, which aligns with the safety constraints mentioned in the PR objectives.


134-144: LGTM!

This test validates the host disk space check. Note that this check may be overly conservative for thin-provisioned qcow2 images (as discussed in the PR comments), but the test correctly validates the current implementation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (1)

409-415: Narrow the exception handling to catch specific validation errors.

The validation logic is correct, but catching Exception is too broad and could hide unexpected errors. Pydantic's TypeAdapter.validate_python raises ValidationError for validation failures.

♻️ Narrow exception handling
+from pydantic import ValidationError
+
 def _validate_size(self, size: str) -> None:
     """Validate size string (e.g., '20G', '512M')."""
     try:
         s = size
         TypeAdapter(ByteSize).validate_python(s + "iB" if s[-1] in "kmgtKMGT" else s)
-    except Exception:
+    except (ValidationError, ValueError):
         raise ValueError(f"Invalid size: '{size}'. Use e.g. '20G', '512M', '2T'") from None
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b884841 and 9bac311.

📒 Files selected for processing (2)
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
🧰 Additional context used
📓 Path-based instructions (4)
packages/jumpstarter-driver-*/jumpstarter_driver_*/driver.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with *Power, Network drivers with *Network, Flasher drivers with *Flasher, Console drivers with *Console, Server drivers with *Server

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
🧠 Learnings (5)
📓 Common learnings
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:196-201
Timestamp: 2026-01-07T14:45:57.173Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk space check before resize is intentionally conservative, checking available space even for thin-provisioned qcow2 images. This is a design decision to prevent potential future issues when the thin-provisioned image is actually filled.
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:289-289
Timestamp: 2026-01-07T14:47:34.243Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk_size configuration only supports absolute size values (e.g., "20G"), not relative sizes (e.g., "+5G"). This is an intentional design decision to keep validation logic straightforward.
📚 Learning: 2026-01-07T14:45:57.173Z
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:196-201
Timestamp: 2026-01-07T14:45:57.173Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), keep the disk space check before resize conservative by validating available space even for thin-provisioned qcow2 images. This helps prevent future issues when the thin image is actually filled. Apply this guideline to all Python files within the QEMU driver package to ensure consistent behavior across related resize operations.

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
📚 Learning: 2026-01-07T14:47:34.243Z
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:289-289
Timestamp: 2026-01-07T14:47:34.243Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk_size configuration only supports absolute size values (e.g., "20G"), not relative sizes (e.g., "+5G"). This is an intentional design decision to keep validation logic straightforward.

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py : Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use `CompositeClient` from `jumpstarter_driver_composite.client` for composite drivers with child drivers.

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `client.py` file containing the client implementation

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
🧬 Code graph analysis (2)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py (2)
  • set_disk_size (23-25)
  • set_memory_size (27-29)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py (5)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (3)
  • client (302-303)
  • set_disk_size (419-422)
  • set_memory_size (426-429)
packages/jumpstarter-driver-composite/jumpstarter_driver_composite/client.py (1)
  • CompositeClient (28-55)
packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/fabric.py (1)
  • FabricAdapter (16-37)
packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/novnc.py (1)
  • NovncAdapter (13-54)
packages/jumpstarter/jumpstarter/client/decorators.py (1)
  • driver_click_group (13-55)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: build
  • GitHub Check: e2e
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.11)
🔇 Additional comments (9)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py (4)

3-3: LGTM!

The imports are appropriate for the CLI functionality being added.

Also applies to: 7-7


23-25: LGTM!

The method correctly delegates to the driver's set_disk_size implementation.


27-29: LGTM!

The method correctly delegates to the driver's set_memory_size implementation.


45-70: CLI implementation follows framework patterns correctly.

The CLI structure follows the driver_click_group pattern demonstrated in CompositeClient and provides clear user feedback. The commands delegate to the validation-enabled setters, which will raise appropriate errors for invalid inputs.

packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (5)

7-7: LGTM!

The additional imports (shutil and ByteSize) are appropriate for the disk resize functionality.

Also applies to: 24-24


173-173: LGTM!

The extraction of current_virtual_size correctly handles both qemu-img info output and raw format fallback.

Also applies to: 182-182


289-289: LGTM!

The disk_size field is appropriately typed and documented. Based on learnings, only absolute size values are supported.


184-211: Resize logic correctly handles disk expansion with appropriate safeguards.

The implementation properly:

  • Parses disk size using ByteSize validation
  • Prevents shrinking operations
  • Checks available space conservatively (intentional design for thin-provisioned images)
  • Passes byte count to qemu-img resize, which accepts raw byte values per QEMU documentation
  • Handles errors with clear messages and stderr capture

The test suite confirms the byte format is correct and the overall logic is sound.


417-422: Validation at setter time is correctly implemented and provides proper feedback.

Both set_disk_size and set_memory_size now validate immediately via _validate_size, ensuring invalid formats like "+5G" (relative sizing) are rejected at call time. The stored formats ("20G", "2G") are compatible with their respective usage contexts:

  • disk_size is correctly converted to bytes for qemu-img resize
  • mem is correctly passed as-is to QEMU's -m flag, which accepts binary format strings

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py:
- Around line 147-164: The test test_resize_succeeds uses a broad except
Exception: pass around the awaited call to driver.children["power"].on(), which
can mask real failures, and then uses a fragile nested-index assertion on
mock_run.call_args_list; fix by mocking Popen to raise the specific expected
exception (or catch that specific exception type) so only the anticipated
failure is suppressed, and replace the brittle index-based check on resize_calls
with a robust inspection of mock_run.call_args_list (e.g., iterate calls,
join/inspect the command args to find the "resize" invocation and assert the
final size argument equals str(20 * 1024**3)) while still asserting that a
resize call exists.
🧹 Nitpick comments (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py (1)

139-139: Consider a more readable mock object pattern.

The type() approach works but is unconventional. Consider using types.SimpleNamespace or unittest.mock.Mock for better readability.

♻️ Alternative approaches

Option 1: Using types.SimpleNamespace:

+from types import SimpleNamespace
+
-    mock_usage = type("U", (), {"free": 5 * 1024**3})()
+    mock_usage = SimpleNamespace(free=5 * 1024**3)

Option 2: Using unittest.mock.Mock:

+from unittest.mock import Mock
+
-    mock_usage = type("U", (), {"free": 5 * 1024**3})()
+    mock_usage = Mock(free=5 * 1024**3)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9bac311 and 2738428.

📒 Files selected for processing (1)
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
🧰 Additional context used
📓 Path-based instructions (2)
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
🧠 Learnings (4)
📓 Common learnings
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:196-201
Timestamp: 2026-01-07T14:45:57.173Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk space check before resize is intentionally conservative, checking available space even for thin-provisioned qcow2 images. This is a design decision to prevent potential future issues when the thin-provisioned image is actually filled.
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:289-289
Timestamp: 2026-01-07T14:47:34.243Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk_size configuration only supports absolute size values (e.g., "20G"), not relative sizes (e.g., "+5G"). This is an intentional design decision to keep validation logic straightforward.
📚 Learning: 2026-01-07T14:45:57.173Z
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:196-201
Timestamp: 2026-01-07T14:45:57.173Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), keep the disk space check before resize conservative by validating available space even for thin-provisioned qcow2 images. This helps prevent future issues when the thin image is actually filled. Apply this guideline to all Python files within the QEMU driver package to ensure consistent behavior across related resize operations.

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py : Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
📚 Learning: 2026-01-07T14:47:34.243Z
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:289-289
Timestamp: 2026-01-07T14:47:34.243Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk_size configuration only supports absolute size values (e.g., "20G"), not relative sizes (e.g., "+5G"). This is an intentional design decision to keep validation logic straightforward.

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: e2e
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (macos-15, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
🔇 Additional comments (3)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py (3)

18-22: LGTM: Consistent async backend configuration.

The anyio_backend fixture ensures all async tests use the same asyncio backend, which improves test reliability and consistency.


113-121: LGTM: Well-structured mock helper.

The mock function properly simulates qemu-img info output with appropriate async behavior and return values.


167-192: LGTM: Comprehensive validation test coverage.

The validation tests effectively cover both valid and invalid inputs for set_disk_size and set_memory_size methods. The test structure is clear, follows pytest conventions, and verifies the expected behavior.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (1)

409-415: LGTM! Validation helper is robust.

The _validate_size helper properly validates size strings and provides clear error messages. The method catches both ValidationError and IndexError to handle edge cases like empty strings.

Minor note: The size parsing logic s + "iB" if s[-1] in "kmgtKMGT" else s appears in both _validate_size (line 413) and QemuPower.on() (line 188). This could optionally be extracted to a tiny helper method to reduce duplication, but given it's just one line in related contexts, the current approach is acceptable.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59b82b9 and 87b67fa.

📒 Files selected for processing (2)
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
🧰 Additional context used
📓 Path-based instructions (3)
packages/jumpstarter-driver-*/jumpstarter_driver_*/driver.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with *Power, Network drivers with *Network, Flasher drivers with *Flasher, Console drivers with *Console, Server drivers with *Server

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
🧠 Learnings (6)
📓 Common learnings
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:196-201
Timestamp: 2026-01-07T14:45:57.173Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk space check before resize is intentionally conservative, checking available space even for thin-provisioned qcow2 images. This is a design decision to prevent potential future issues when the thin-provisioned image is actually filled.
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:289-289
Timestamp: 2026-01-07T14:47:34.243Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk_size configuration only supports absolute size values (e.g., "20G"), not relative sizes (e.g., "+5G"). This is an intentional design decision to keep validation logic straightforward.
📚 Learning: 2026-01-07T14:45:57.173Z
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:196-201
Timestamp: 2026-01-07T14:45:57.173Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), keep the disk space check before resize conservative by validating available space even for thin-provisioned qcow2 images. This helps prevent future issues when the thin image is actually filled. Apply this guideline to all Python files within the QEMU driver package to ensure consistent behavior across related resize operations.

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
📚 Learning: 2026-01-07T14:47:34.243Z
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:289-289
Timestamp: 2026-01-07T14:47:34.243Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk_size configuration only supports absolute size values (e.g., "20G"), not relative sizes (e.g., "+5G"). This is an intentional design decision to keep validation logic straightforward.

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py : Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `driver.py` file containing the driver implementation

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: After driver creation, implement driver logic in `driver.py`, add tests in `driver_test.py`, update README.md with specific documentation, and test the driver

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
🧬 Code graph analysis (2)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py (2)
  • set_disk_size (23-25)
  • set_memory_size (27-29)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (3)
  • on (73-250)
  • set_disk_size (419-422)
  • set_memory_size (426-429)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: build
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: e2e
  • GitHub Check: pytest-matrix (macos-15, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
🔇 Additional comments (12)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (5)

7-7: LGTM! Imports are properly used.

The shutil import is needed for disk space checking (line 196), and ValidationError is needed for size validation (line 414).

Also applies to: 24-24


173-173: LGTM! Robust virtual size detection.

The code properly tracks the current virtual size with appropriate fallbacks when qemu-img info fails or doesn't provide the information.

Also applies to: 182-182


184-211: LGTM! Resize logic is comprehensive and handles edge cases.

The implementation correctly:

  • Prevents shrinking operations (lines 190-194)
  • Validates available disk space before resizing (lines 196-201)
  • Only resizes when necessary (line 203)
  • Reports qemu-img errors clearly (line 211)

Based on learnings, the conservative space check for thin-provisioned images is intentional.


289-289: LGTM! Field correctly documented.

The disk_size field comment correctly shows absolute size format only, which aligns with the design decision.

Based on learnings, only absolute sizes are supported.


417-429: LGTM! Setters provide early validation.

Both set_disk_size and set_memory_size properly validate input before assignment, providing immediate feedback to users. This addresses previous feedback about validating at command send time rather than waiting until boot.

The validation ensures invalid formats like "invalid" are caught early with clear error messages.

packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py (7)

1-1: LGTM! Test infrastructure properly set up.

The import additions support the new mocking and async test functionality, and the anyio_backend fixture ensures consistent async behavior across tests.

Also applies to: 7-7, 18-21


104-120: LGTM! Fixture provides proper cleanup.

The resize_test fixture properly creates test drivers and ensures cleanup of temporary directories. The explicit cleanup in lines 119-120 is a defensive best practice, even though garbage collection would typically handle it.


123-131: LGTM! Mock helper is well-structured.

The _mock_qemu_img_info helper properly simulates qemu-img info output with the necessary fields for testing resize logic.


134-141: LGTM! Shrink prevention properly tested.

The test correctly verifies that attempting to shrink a disk raises a RuntimeError with an appropriate message.


144-154: LGTM! Insufficient space check properly tested.

The test correctly verifies that attempting to resize beyond available disk space raises a RuntimeError with a clear message about insufficient space.


157-174: LGTM! Resize success path properly tested.

The test verifies that the resize flow executes correctly and calls qemu-img resize with the expected size argument. The improved exception handling (lines 166-168) and assertion robustness (lines 171-174) address previous review feedback well.


177-202: LGTM! Setter validation comprehensively tested.

The four tests properly verify that both set_disk_size and set_memory_size accept valid size strings and reject invalid ones with clear error messages. This ensures the early validation logic works as intended.

@evakhoni
Copy link
Contributor Author

evakhoni commented Jan 7, 2026

@bennyz @bkhizgiy ok, seems to be ready now.
in this one, I limited to absolute size for simplicity, and also blocked shrinking. implemented some test coverage for basic usage. feel free to comment suggestions if any..

Copy link
Contributor

@bkhizgiy bkhizgiy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good, added a nitpick command but approved by me :)

f"requested {self.parent.disk_size}"
)

available = shutil.disk_usage(root.parent).free
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could exhaust all the resources on the host, but i think the issue here goes deeper, since if we consider multi tenancy a single client can overcommit and make the host unusable for others. perhaps it is not for this PR, but maybe we need to set hard limits, perhaps as some percentage of what is available

Copy link
Contributor Author

@evakhoni evakhoni Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah that's pretty easy to implement, we just need a consensus on the maximum percentage, or the host reserved amount, or both. I don't mind including it in this PR as well if we all agree on the amounts.

speaking about potential issues, it goes deeper than that, because iiuc our free space calculation will not take into account the virtual size of other thin-provisioned guests on that host, and assuming they're all stored in the same partition, each of them theoretically allowed to grow not taking into account the growth of the others.. however, in order to address that properly, we'll probably have to either iterate over all other images stored in the host at runtime, or alternatively maintain a data structure storing this, and update it at each resize, both of which seems to me like an overkill for such a niche feature. maybe I'm shooting myself in the foot writing this one 😆 but it had to be said.

lastly, ideally we should not be implementation-specific on the user-facing side, but rather let the user make a lease request with his needs, and figure out on our side what we can do in order to satisfy it, if the resources and user permissions allow it (something similar to https://github.com/jumpstarter-dev/jumpstarter/issues/724)

that being said, it's a slippery slope and a deep rabbit hole, not sure if we have the resources to dive into that one 😉

anyway, let me know what you think about implementing a percentage or something, in this (or a follow up) PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, some is left for the lab admin to decide the configuration of the host, and how many exporters they set up on each host, and how much each gets, and whether they set a separate partition for jumpstarter storage

another problem however is that it would be easy to violate the quota constraints without trying too hard when the image is a compressed qcow2 with a large virtual size

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, this one at least have "some" constrains :) we're probably need to allow less than (root.parent).free for a safe margin, but iiuc without this PR just flashing a qemu image have no limits at all, so this one's a step in a good direction in a sense 😉
so what do you suggest?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not saying it's bad, i'm just thinking out loud :) let's merge for now and circle back
so please squash the commits and i'll merge

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to squash manually via rebase then force-push, or there's any facilities available for it in the repo?
I thought Allow squash merging is enabled in this repo so you can choice it from your side while you merge?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

force push...

yeah we just use merge commits too so i don't want to make it inconsistent

Copy link
Contributor Author

@evakhoni evakhoni Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok done 👌 squashed into 3 separated by functionality. the first adds the resize, second one is the j command and third have the tests. fine like that or have to be one?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (1)

407-426: Well-designed size parsing and setter methods.

The implementation is clean and correct:

  • _parse_size (lines 407-412): Clever logic that appends "iB" to bare unit letters (e.g., "20G" → "20GiB") to force binary interpretation, while passing through explicit formats ("20GiB", "20GB") for ByteSize to parse. Error handling properly catches invalid inputs with helpful error messages.

  • set_disk_size and set_memory_size (lines 414-426): Both follow a consistent pattern of validating input before storing. The validation ensures the size string is parseable without converting it, allowing QEMU to interpret the format directly in the command line.

The @validate_call decorators provide additional safety for RPC calls, and the docstrings clearly explain each method's purpose.

Optional: Add inline comment explaining the "iB" append logic

Consider adding a brief comment at line 410 to explain why "iB" is appended:

 def _parse_size(self, size: str) -> int:
     """Parse size string (e.g., '20G') to bytes."""
     try:
+        # Append 'iB' to bare unit letters (G→GiB) to force binary interpretation
         return int(TypeAdapter(ByteSize).validate_python(size + "iB" if size[-1] in "kmgtKMGT" else size))
     except (ValidationError, IndexError):
         raise ValueError(f"Invalid size: '{size}'. Use e.g. '20G', '512M', '2T'") from None

This is optional as the logic is relatively straightforward.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f225a2 and be4d299.

📒 Files selected for processing (3)
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver_test.py
  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py
🧰 Additional context used
📓 Path-based instructions (3)
packages/jumpstarter-driver-*/jumpstarter_driver_*/driver.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with *Power, Network drivers with *Network, Flasher drivers with *Flasher, Console drivers with *Console, Server drivers with *Server

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

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

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
🧠 Learnings (3)
📓 Common learnings
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:196-201
Timestamp: 2026-01-07T14:45:57.173Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk space check before resize is intentionally conservative, checking available space even for thin-provisioned qcow2 images. This is a design decision to prevent potential future issues when the thin-provisioned image is actually filled.
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:289-289
Timestamp: 2026-01-07T14:47:34.243Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk_size configuration only supports absolute size values (e.g., "20G"), not relative sizes (e.g., "+5G"). This is an intentional design decision to keep validation logic straightforward.
📚 Learning: 2026-01-07T14:45:57.173Z
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:196-201
Timestamp: 2026-01-07T14:45:57.173Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), keep the disk space check before resize conservative by validating available space even for thin-provisioned qcow2 images. This helps prevent future issues when the thin image is actually filled. Apply this guideline to all Python files within the QEMU driver package to ensure consistent behavior across related resize operations.

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
📚 Learning: 2026-01-07T14:47:34.243Z
Learnt from: evakhoni
Repo: jumpstarter-dev/jumpstarter PR: 793
File: packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py:289-289
Timestamp: 2026-01-07T14:47:34.243Z
Learning: In the QEMU driver (jumpstarter_driver_qemu), the disk_size configuration only supports absolute size values (e.g., "20G"), not relative sizes (e.g., "+5G"). This is an intentional design decision to keep validation logic straightforward.

Applied to files:

  • packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py
🧬 Code graph analysis (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (1)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/client.py (2)
  • set_disk_size (23-25)
  • set_memory_size (27-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: build
  • GitHub Check: e2e
  • GitHub Check: pytest-matrix (macos-15, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.13)
🔇 Additional comments (3)
packages/jumpstarter-driver-qemu/jumpstarter_driver_qemu/driver.py (3)

7-7: LGTM - Import additions support disk resize functionality.

The new imports are appropriately used for disk space checking (shutil.disk_usage) and size validation (TypeAdapter, ByteSize from pydantic).

Also applies to: 24-24


287-287: LGTM - Clear field definition with appropriate typing.

The disk_size field is well-documented and correctly typed as optional. The inline comment clearly explains its purpose.

Based on learnings: This field only supports absolute size values (e.g., "20G"), not relative sizes (e.g., "+5G"), which is an intentional design decision for straightforward validation.


173-173: Well-implemented disk resize flow with comprehensive validation.

The implementation correctly handles disk resizing before VM boot:

  • Obtains current virtual disk size from image metadata with fallback to file size
  • Prevents shrinking operations with a clear error message
  • Performs conservative disk space validation—intentionally checks full requested size even for thin-provisioned images per design decision
  • Only performs resize when expansion is needed, avoiding unnecessary operations
  • Properly forwards qemu-img errors to the user

The byte count format passed to qemu-img resize is correct; the test suite validates this format with assert resize_cmd[-1] == str(20 * 1024**3).

@bennyz bennyz merged commit ac364c4 into jumpstarter-dev:main Jan 8, 2026
18 checks passed
@evakhoni evakhoni deleted the qemu_resize branch January 8, 2026 17:48
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants