Skip to content

Conversation

@meinaLi
Copy link
Contributor

@meinaLi meinaLi commented Dec 11, 2025

Automate cases:
VIRT-306532 - [Disk snapshot][manual mode] Use virsh cmd to create disk snapshot with multiple 'manual' disk snapshot modes

This case is to verify the consistency and usability of 'manual' disk snapshot mode from libvirt-11.9.0-1

Summary by CodeRabbit

  • Tests
    • Added a new test configuration and comprehensive test suite for manual snapshot creation with disk-specific snapshot behaviors.
    • Validates bitmap flags, snapshot metadata options and image state.
    • Verifies VM state transitions around snapshot operations (pause/resume).
    • Covers single- and multi-snapshot scenarios with varied disk snapshot modes.

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

Walkthrough

Adds a new Libvirt test for manual snapshot creation: a config file defining test parameters and two variants (one_manual_snapshot, three_manual_snapshots) and a Python test module that prepares a VM with extra QCOW2 disks, creates manual snapshots with per-disk snapshot options, validates qemu-img bitmap/checkpoint info, checks VM state transitions (pause/resume) around snapshot actions, and performs cleanup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • New Python module with multiple functions coordinating VM/disk preparation, snapshot creation, qemu-img parsing, and teardown — review for correctness of disk handling, command execution, return/error handling, and cleanup.
  • Validate qemu-img output parsing logic (bitmap/auto flag detection) and edge-case handling.
  • Check consistency between cfg variants and parameter use in the Python code (disk lists, snapshot options, checkpoint/snap names).

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title contains a typo ('mew' instead of 'new') and is related to the changeset, but only partially addresses the main changes: it mentions 'manual snapshot cases' but lacks specificity about the actual scope (new test configuration and implementation for manual disk snapshots). Correct the typo to 'new' and consider a more descriptive title such as 'Snapshot: add manual snapshot test cases with multiple disk modes' to better reflect the scope of the changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Copy link

@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

🧹 Nitpick comments (1)
libvirt/tests/src/snapshot/create_manual_snapshot.py (1)

104-106: Consider a more precise check for the "auto" flag.

The check "auto" in line could produce false positives if a bitmap name or other field contains the substring "auto". Consider checking specifically for the flags line format.

         # Check for auto flag
-        elif "auto" in line:
+        elif line.startswith("flags:") and "auto" in line:
             current_bitmap_has_auto = True
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c40e1a4 and 97b02ef.

📒 Files selected for processing (2)
  • libvirt/tests/cfg/snapshot/create_manual_snapshot.cfg (1 hunks)
  • libvirt/tests/src/snapshot/create_manual_snapshot.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: meinaLi
Repo: autotest/tp-libvirt PR: 6628
File: libvirt/tests/src/virtual_disks/virtual_disks_io_error.py:128-129
Timestamp: 2025-10-24T06:35:25.679Z
Learning: In libvirt tests, `virsh.snapshot_delete()` without the `--metadata` flag automatically deletes both snapshot metadata and external disk files. Explicit `os.remove()` is only needed when using `virsh.snapshot_delete()` with the `--metadata` flag, which removes only metadata.
📚 Learning: 2025-10-24T06:35:25.679Z
Learnt from: meinaLi
Repo: autotest/tp-libvirt PR: 6628
File: libvirt/tests/src/virtual_disks/virtual_disks_io_error.py:128-129
Timestamp: 2025-10-24T06:35:25.679Z
Learning: In libvirt tests, `virsh.snapshot_delete()` without the `--metadata` flag automatically deletes both snapshot metadata and external disk files. Explicit `os.remove()` is only needed when using `virsh.snapshot_delete()` with the `--metadata` flag, which removes only metadata.

Applied to files:

  • libvirt/tests/src/snapshot/create_manual_snapshot.py
🧬 Code graph analysis (1)
libvirt/tests/src/snapshot/create_manual_snapshot.py (2)
provider/virtual_disk/disk_base.py (2)
  • get_source_list (56-93)
  • add_vm_disk (95-112)
provider/snapshot/snapshot_base.py (1)
  • SnapshotTest (16-101)
🪛 Ruff (0.14.8)
libvirt/tests/src/snapshot/create_manual_snapshot.py

54-54: Unused function argument: disk_paths

(ARG001)


226-226: Do not catch blind exception: Exception

(BLE001)


235-235: Do not catch blind exception: Exception

(BLE001)


242-242: Local variable snap_name is assigned to but never used

Remove assignment to unused variable snap_name

(F841)

⏰ 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: Python 3.12
  • GitHub Check: Python 3.11
  • GitHub Check: Python 3.8
  • GitHub Check: Python 3.9
🔇 Additional comments (5)
libvirt/tests/cfg/snapshot/create_manual_snapshot.cfg (1)

1-18: LGTM!

The configuration is well-structured with appropriate version gating for libvirt 11.9.0 and clear variant definitions for the two test scenarios. The diskspec options correctly differentiate between the single manual snapshot case (vda=no, vdb=manual, vdc=external) and the multiple manual snapshots case (all disks=manual).

libvirt/tests/src/snapshot/create_manual_snapshot.py (4)

19-51: LGTM!

The guest preparation logic correctly creates additional disk images and attaches them to the VM, skipping vda (the base system disk).


117-160: LGTM!

The validation logic correctly differentiates between test variants and properly checks for bitmap presence and auto flags on the expected disks.


163-188: LGTM!

The state transition validation correctly verifies the VM is paused after manual snapshot creation and resumes successfully.


217-237: Verify or explicitly document snapshot overlay cleanup with test_obj.teardown_test().

The test creates snapshots with --no-metadata --disk-only options, which generates external snapshot overlay files. The teardown removes the original test disk images and calls test_obj.teardown_test(), which invokes libvirt.clean_up_snapshots() from the virttest framework.

However, the teardown does not explicitly list or delete snapshots before relying on clean_up_snapshots(). According to libvirt behavior, virsh.snapshot_delete() without the --metadata flag automatically removes external snapshot files, so clean_up_snapshots() should handle this. Consider adding a comment or explicit snapshot deletion in the teardown to clarify this dependency, or verify in testing that no orphaned snapshot overlay files remain after teardown completes.

Automate cases:
VIRT-306532 - [Disk snapshot][manual mode] Use virsh cmd to create disk
snapshot with multiple 'manual' disk snapshot modes

This case is to verify the consistency and usability of 'manual' disk
snapshot mode from libvirt-11.9.0-1

Signed-off-by: Meina Li <[email protected]>
Copy link

@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: 1

🧹 Nitpick comments (3)
libvirt/tests/src/snapshot/create_manual_snapshot.py (3)

54-70: Consider checking snapshot_create_as result and failing early on error.

Right now the return value of virsh.snapshot_create_as() is ignored; if the command fails, the test will continue and later assertions may be misleading.

You can make failures clearer by asserting on the exit status:

-    virsh.snapshot_create_as(vm_name, full_snap_options, **virsh_dargs)
-    test.log.debug(f"Created snapshot {snap_name} with options: {full_snap_options}")
+    result = virsh.snapshot_create_as(vm_name, full_snap_options, **virsh_dargs)
+    test.log.debug(f"Snapshot create cmd: virsh snapshot-create-as {vm_name} {full_snap_options}")
+    if result.exit_status != 0:
+        test.fail(f"Failed to create snapshot {snap_name}: {result.stderr_text}")
+    test.log.debug(f"Created snapshot {snap_name} with options: {full_snap_options}")

This keeps the behavior deterministic when virsh fails.


116-159: check_image_info logic matches cfg variants; consider negative checks as a future enhancement.

The mapping from only_one_manual / with_multiple_manual to expected_auto_disks and the use of disk_paths look correct and consistent with the cfg (vda, vdb, vdc).

If you want stricter validation later, you could also assert that non-expected disks do not contain the checkpoint bitmap or auto flag, but that’s not required for this PR.


216-236: Teardown: broad exception catches and missing virsh_dargs on checkpoint ops.

Two minor points in teardown_test():

  1. Broad except Exception for both checkpoint deletion and os.remove() may hide unexpected issues during cleanup.
  2. checkpoint_list/checkpoint_delete are called without **virsh_dargs, so their behavior (debugging, status handling) is slightly inconsistent with the rest of the file.

If you want to tighten this up while keeping teardown robust:

-        try:
-            checkpoints = virsh.checkpoint_list(vm_name)
-            if checkpoint_name in checkpoints.stdout:
-                virsh.checkpoint_delete(vm_name, checkpoint_name)
-        except Exception as e:
-            test.log.debug(f"Error cleaning checkpoint: {e}")
+        try:
+            checkpoints = virsh.checkpoint_list(vm_name, **virsh_dargs)
+            if checkpoint_name in checkpoints.stdout:
+                virsh.checkpoint_delete(vm_name, checkpoint_name, **virsh_dargs)
+        except Exception as e:  # consider narrowing to specific virsh/disk errors
+            test.log.debug(f"Error cleaning checkpoint {checkpoint_name}: {e}")
@@
-        for disk_name in test_disks:
-            img_path = disk_paths.get(disk_name)
-            if img_path and os.path.exists(img_path):
-                try:
-                    os.remove(img_path)
-                except Exception as e:
-                    test.log.debug(f"Error removing image {img_path}: {e}")
+        for disk_name in test_disks:
+            img_path = disk_paths.get(disk_name)
+            if img_path and os.path.exists(img_path):
+                try:
+                    os.remove(img_path)
+                except OSError as e:
+                    test.log.debug(f"Error removing image {img_path}: {e}")

Narrowing the second except to OSError addresses the static-analysis concern there; for the checkpoint path, you can decide which specific error types you’re comfortable swallowing in teardown.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97b02ef and 02ca254.

📒 Files selected for processing (2)
  • libvirt/tests/cfg/snapshot/create_manual_snapshot.cfg (1 hunks)
  • libvirt/tests/src/snapshot/create_manual_snapshot.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: meinaLi
Repo: autotest/tp-libvirt PR: 6628
File: libvirt/tests/src/virtual_disks/virtual_disks_io_error.py:128-129
Timestamp: 2025-10-24T06:35:25.679Z
Learning: In libvirt tests, `virsh.snapshot_delete()` without the `--metadata` flag automatically deletes both snapshot metadata and external disk files. Explicit `os.remove()` is only needed when using `virsh.snapshot_delete()` with the `--metadata` flag, which removes only metadata.
📚 Learning: 2025-10-24T06:35:25.679Z
Learnt from: meinaLi
Repo: autotest/tp-libvirt PR: 6628
File: libvirt/tests/src/virtual_disks/virtual_disks_io_error.py:128-129
Timestamp: 2025-10-24T06:35:25.679Z
Learning: In libvirt tests, `virsh.snapshot_delete()` without the `--metadata` flag automatically deletes both snapshot metadata and external disk files. Explicit `os.remove()` is only needed when using `virsh.snapshot_delete()` with the `--metadata` flag, which removes only metadata.

Applied to files:

  • libvirt/tests/src/snapshot/create_manual_snapshot.py
🧬 Code graph analysis (1)
libvirt/tests/src/snapshot/create_manual_snapshot.py (2)
provider/virtual_disk/disk_base.py (2)
  • get_source_list (56-93)
  • add_vm_disk (95-112)
provider/snapshot/snapshot_base.py (1)
  • SnapshotTest (16-101)
🪛 Ruff (0.14.8)
libvirt/tests/src/snapshot/create_manual_snapshot.py

225-225: Do not catch blind exception: Exception

(BLE001)


234-234: Do not catch blind exception: Exception

(BLE001)

⏰ 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: Python 3.12
  • GitHub Check: Python 3.9
  • GitHub Check: Python 3.11
  • GitHub Check: Python 3.8
🔇 Additional comments (4)
libvirt/tests/src/snapshot/create_manual_snapshot.py (2)

19-51: Guest preparation and disk wiring look consistent with cfg.

prepare_guest() correctly aligns with the cfg: it derives vda from DiskBase.get_source_list, creates vdb/vdc under data_dir.get_tmp_dir(), and wires them via disk_dict and disk_obj.add_vm_disk(). The disk_paths map you return matches how check_image_info() and teardown_test() consume it, so this part looks good.


162-188: VM state checks are straightforward; confirm expected paused state semantics.

The use of virsh.domstate to assert "paused" after snapshot and "running" after virsh.resume is clear and makes failures easy to diagnose.

Just ensure that, for the libvirt/QEMU versions you’re targeting, the snapshot flow for this manual mode is actually expected to pause the guest (vs. e.g. staying running or using a different state string), otherwise the check may be too strict.

libvirt/tests/cfg/snapshot/create_manual_snapshot.cfg (2)

1-1: Double‑check whether the top‑level cfg key should have a leading dash.

The header line is:

- snapshot_create.manual_snapshot:

In many tp-libvirt cfg files, the leading - is typically used under variants: rather than on the top‑level test key. If that’s the case here, this line might need to be:

snapshot_create.manual_snapshot:

to be parsed as a test entry instead of a list item.

Please compare with existing snapshot_create.* cfgs in this repo to confirm the intended syntax.


5-18: Cfg parameters align well with the Python test implementation.

The cfg fields (disk_list, disk_type, disk_dict, checkpoint_name, snap_name, snap_options, only_one_manual, with_multiple_manual, and diskspec_options) line up correctly with create_manual_snapshot.py:

  • disk_list/disk_dict use the %s placeholder as expected by prepare_guest().
  • only_one_manual and with_multiple_manual match the flags consumed by check_image_info().
  • snap_options and per‑variant diskspec_options are combined in create_snapshots() as intended.

This wiring looks consistent and should make the two variants behave as described.

Comment on lines +73 to +113
def check_bitmap_auto_flag(result, checkpoint_name):
"""
Check if bitmap has auto flag in qemu-img info output.

:param result: qemu-img info output string
:param checkpoint_name: name of the checkpoint to check
:return: True if auto flag found, False otherwise
"""
lines = result.split('\n')
in_bitmaps_section = False
current_bitmap_has_auto = False
current_bitmap_name = None

for line in lines:
line = line.strip()

# Check if we're in the bitmaps section
if line == "bitmaps:":
in_bitmaps_section = True
continue

# If not in bitmaps section, skip
if not in_bitmaps_section:
continue

# Check for start of a new bitmap entry
if re.match(r'^\[\d+\]:$', line):
# Reset for new bitmap
current_bitmap_has_auto = False
current_bitmap_name = None
# Check for auto flag
elif "auto" in line:
current_bitmap_has_auto = True
# Check for bitmap name
elif line.startswith("name:"):
name_value = line.split("name:", 1)[1].strip()
current_bitmap_name = name_value
# If this is the target checkpoint and it has auto flag, return True
if current_bitmap_name == checkpoint_name and current_bitmap_has_auto:
return True
return False
Copy link

@coderabbitai coderabbitai bot Dec 11, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Bitmap auto flag detection can miss matches if name precedes auto in the output.

The current logic only checks for a match when processing a name: line if current_bitmap_has_auto is already True. If qemu-img info outputs name: before the line containing auto, the function never detects the match even though the bitmap has the auto flag, causing false negatives. Additionally, the last bitmap entry is never checked after the loop completes.

The suggested refactor correctly addresses the primary issue by checking at the start of each new bitmap entry (to finalize the previous one) and again at the end of the loop. However, use a more specific pattern to detect the auto flag—such as line.startswith("auto:") or re.search(r'auto:\s*true', line)—rather than just "auto" in line, which could produce false positives if "auto" appears in other contexts (e.g., flags: auto_grow).

🤖 Prompt for AI Agents
In libvirt/tests/src/snapshot/create_manual_snapshot.py around lines 73 to 113,
the bitmap `auto` detection misses cases when the `name:` line appears before
the `auto` line and never validates the final bitmap; update the loop to (1)
finalize/check the previous bitmap when a new bitmap entry starts (so if we
encounter a new index we evaluate the prior bitmap's name+auto state), (2) use a
stricter detection for the auto flag (e.g., match lines starting with "auto:" or
use a regex like r'auto:\s*true' instead of `"auto" in line`), and (3) after the
loop completes, perform a final check of the last bitmap's name+auto state and
return True if it matches the target checkpoint; otherwise return False.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think no need to update it because the auto is always before the name:.

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🌐 Web query:

qemu-img info bitmap output format auto flag

💡 Result:

Short answer:

  • qemu-img info can show qcow2 bitmap entries under "Format specific information". Use --output (or --output=OFMT) to choose human or json output (OFMT = human | json). [1][4]

  • The bitmap "flags" field lists values such as "in-use" and "auto". "auto" means the bitmap is enabled to track all writes to the virtual disk (i.e., it must reflect all changes by any writer). "in-use" means the bitmap was not flushed/synced and may be inconsistent. These meanings come from the qcow2 spec / QMP docs. [2][3]

References:

  1. qemu-img documentation (info / --output). [1]
  2. qcow2 image format — bitmap flags and semantics (auto/in_use). [2]
  3. QEMU QMP / qcow2 bitmap flags descriptions. [3]
  4. qemu-img patch/example showing bitmap output in qemu-img info. [4]

🌐 Web query:

qemu-img info bitmaps example output format structure

💡 Result:

qemu-img info (human or JSON) includes a "Format specific information" section that lists persistent bitmaps for formats that support them (e.g., qcow2). Structure and key fields:

  • Top-level placement

    • In human output: under "Format specific information:" with a "bitmaps:" subsection. [1]
    • In JSON output: bitmaps appear in the ImageInfo object (as format-specific fields). [1]
  • Per-bitmaps (human output example structure)

    • bitmaps:
      [i]:
      flags:
      [0]: in-use
      [1]: auto
      name:
      unknown flags:
      granularity:
    • "flags" lists individual flag names; "name" is the bitmap identifier; "granularity" is the bitmap granularity in bytes. Example shown in upstream discussion: two bitmap entries with flags, names and granularity. [2]
  • JSON fields (semantics)

    • Entries include booleans/meta indicating whether sectors contain data/zero/present when listing mappings; qemu-img --output=json uses the ImageInfo QAPI type and includes data/zero/present for mapping ranges. Use --output=json to get machine-parseable fields. [1]

References:
[1] QEMU/qemu-img documentation (qemu-img info output description and --output=json/ImageInfo).
[2] qemu-block mailing-list patch example showing human output bitmaps block with flags/name/granularity.


@meinaLi, you're correct about the output order! Based on the qemu-img info format documentation, the flags: section (which contains auto) does consistently appear before the name: field in each bitmap entry, so the current logic handles that ordering properly.

However, there are two other considerations:

  1. Last bitmap entry: After the loop completes, the final bitmap's name and auto flag are never checked. If the last bitmap in the output matches checkpoint_name and has the auto flag, the function would return False instead of True.

  2. Detection specificity: The check "auto" in line could potentially match unintended text. Using something more specific like line.strip() == "auto" or checking within the flags context would be more robust.

If you're confident the current implementation works correctly for your test scenarios, that's fine. These are just observations for potential edge cases.


🧠 Learnings used
Learnt from: meinaLi
Repo: autotest/tp-libvirt PR: 6628
File: libvirt/tests/src/virtual_disks/virtual_disks_io_error.py:128-129
Timestamp: 2025-10-24T06:35:25.679Z
Learning: In libvirt tests, `virsh.snapshot_delete()` without the `--metadata` flag automatically deletes both snapshot metadata and external disk files. Explicit `os.remove()` is only needed when using `virsh.snapshot_delete()` with the `--metadata` flag, which removes only metadata.

@nanli1 nanli1 self-requested a review December 11, 2025 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant