Skip to content

Conversation

@SimonFair
Copy link
Contributor

@SimonFair SimonFair commented Jan 8, 2026

Summary by CodeRabbit

  • Bug Fixes
    • Snapshots with missing or deleted backing files are now properly removed from the system.
    • Snapshot refresh operations now correctly handle cleanup during destructive VM operations.
    • Improved NVRAM cleanup coordination alongside snapshot management.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

Walkthrough

Added a helper function to purge deleted snapshots and enhanced the snapshot refresh function with conditional purging. Modified disk backing file reference mappings from device-based to diskid-based keys. Code blocks reordered to ensure purge operations occur before NVRAM cleanup.

Changes

Cohort / File(s) Summary
Snapshot Management Enhancement
emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php
Added purge_deleted_snapshots() helper to remove snapshot entries with missing disk backing files. Updated refresh_snapshots_database() signature with optional $delete_used parameter. Changed disk backing mapping from disk["device"] to diskid-based keys and updated source path references to disk["source"]["@attributes"]["file"]. Conditional purge integration based on destructive actions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Snapshots dancing in the VM night,
Deleted files fade from sight,
A purge helper comes to clean,
Backing maps now pristine,
Database fresh, diskid's right!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 title clearly references the primary change (snapshot updates for block commit) which aligns with the main modifications to snapshot handling and purge logic in the libvirt_helpers.php file.

✏️ 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.

@github-actions
Copy link

github-actions bot commented Jan 8, 2026

🔧 PR Test Plugin Available

A test plugin has been generated for this PR that includes the modified files.

Version: 2026.01.08.1558
Build: View Workflow Run

📥 Installation Instructions:

Install via Unraid Web UI:

  1. Go to Plugins → Install Plugin
  2. Copy and paste this URL:
https://preview.dl.unraid.net/pr-plugins/pr-2508/webgui-pr-2508.plg
  1. Click Install

Alternative: Direct Download

⚠️ Important Notes:

  • Testing only: This plugin is for testing PR changes
  • Backup included: Original files are automatically backed up
  • Easy removal: Files are restored when plugin is removed
  • Conflicts: Remove this plugin before installing production updates

📝 Modified Files:

Click to expand file list
emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php

🔄 To Remove:

Navigate to Plugins → Installed Plugins and remove webgui-pr-2508, or run:

plugin remove webgui-pr-2508

🤖 This comment is automatically generated and will be updated with each new push to this PR.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php (1)

1980-1989: Key mismatch: backing array uses $diskid but access uses $disk["device"].

The backing array is now keyed by $diskid (from $disk["@attributes"]["name"]) at line 1980, but line 1985 still accesses it using $disk["device"]. This inconsistency will cause $parentfind to be null, breaking parent snapshot detection.

🐛 Proposed fix
-            $snaps[$vmsnap]['backing'][$diskid] = $output;
-            $rev = "r".$diskid;
+            $snaps[$vmsnap]['backing'][$diskid] = $output;
+            $rev = "r".$diskid;
             $reversed = array_reverse($output);
             $snaps[$vmsnap]['backing'][$rev] = $reversed;
         }
-        $parentfind = $snaps[$vmsnap]['backing'][$disk["device"]];
+        $parentfind = $snaps[$vmsnap]['backing'][$diskid] ?? [];
         $parendfileinfo = pathinfo($parentfind[1]);

Note: You'll need to preserve $diskid from the last loop iteration or refactor to track the last disk ID explicitly.

🤖 Fix all issues with AI agents
In @emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php:
- Around line 1960-1963: Guard against null $snaps before calling
purge_deleted_snapshots(): when json_decode() can return null, wrap the call so
it only runs if $delete_used is true AND $snaps is an array (e.g. if
($delete_used && is_array($snaps)) purge_deleted_snapshots($snaps);), or
explicitly convert/validate $snaps to an array before calling
purge_deleted_snapshots() so the function's array type hint is never passed
null.
- Around line 1934-1951: The purge_deleted_snapshots function assumes $snaps is
a non-null array and that each $snap contains an iterable 'disks' key; add a
null/type check at the top to return early or cast to an array if $snaps is
null/invalid, and inside the foreach over $snaps ensure isset($snap['disks']) &&
is_array($snap['disks']) before iterating; for malformed entries
(missing/invalid 'disks') skip/unset them, and only access
$disk['source']['@attributes']['file'] after verifying those keys exist before
calling file_exists.
🧹 Nitpick comments (2)
emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php (2)

2517-2517: Simplify redundant ternary expression.

The comparison $action=="yes" already evaluates to a boolean. The ternary wrapper is unnecessary.

♻️ Suggested simplification
-        refresh_snapshots_database($vm, $action=="yes" ? true : false);
+        refresh_snapshots_database($vm, $action === "yes");

2617-2617: Simplify redundant ternary expression (same as blockcommit).

♻️ Suggested simplification
-    refresh_snapshots_database($vm,$action=="yes" ? true : false);
+    refresh_snapshots_database($vm, $action === "yes");
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 957fa3a and 01d1fb4.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Squidly271
Repo: unraid/webgui PR: 2333
File: emhttp/plugins/dynamix/include/publish.php:28-36
Timestamp: 2025-08-13T03:13:53.283Z
Learning: In the unraid/webgui codebase, PR #2333 by Squidly271 supersedes earlier work that already addressed all legacy publish() calls using the old overloaded syntax. There are no remaining legacy calls that need backward compatibility handling in the publish() function.
📚 Learning: 2025-12-28T15:54:58.673Z
Learnt from: mgutt
Repo: unraid/webgui PR: 2496
File: emhttp/plugins/dynamix/Browse.page:901-906
Timestamp: 2025-12-28T15:54:58.673Z
Learning: In the unraid/webgui codebase, CSRF validation is centralized in the global auto_prepend_file (local_prepend.php) which runs before every PHP request. Do not add per-script CSRF checks in individual files like Browse.page or Control.php. If a script relies on global validation, ensure it does not duplicate CSRF logic; otherwise extend the central preface to cover the needed checks.

Applied to files:

  • emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php
🧬 Code graph analysis (1)
emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php (1)
emhttp/plugins/dynamix.vm.manager/include/libvirt.php (3)
  • get_disk_stats (1225-1284)
  • get_domain_by_name (1493-1496)
  • domain_get_uuid (1691-1696)

Comment on lines +1934 to +1951
function purge_deleted_snapshots(array &$snaps){
foreach ($snaps as $snapname => $snap) {
$broken = false;

function refresh_snapshots_database($vm) {
foreach ($snap['disks'] as $disk) {
$snapfile = $disk['source']['@attributes']['file'];

if (!file_exists($snapfile)) {
$broken = true;
break;
}
}

if ($broken) {
unset($snaps[$snapname]);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add null safety checks for snapshot array access.

The function assumes $snap['disks'] always exists and is iterable. If a snapshot entry is malformed or missing the disks key, this will throw a warning/error. Additionally, the caller may pass null if json_decode() fails (e.g., file doesn't exist or contains invalid JSON), which will fail the array type hint.

🛠️ Suggested fix
 function purge_deleted_snapshots(array &$snaps){
+    if (!is_array($snaps)) {
+        return;
+    }
     foreach ($snaps as $snapname => $snap) {
         $broken = false;
 
+        if (!isset($snap['disks']) || !is_array($snap['disks'])) {
+            continue;
+        }
+
         foreach ($snap['disks'] as $disk) {
-            $snapfile = $disk['source']['@attributes']['file'];
+            $snapfile = $disk['source']['@attributes']['file'] ?? null;
 
-            if (!file_exists($snapfile)) {
+            if (empty($snapfile) || !file_exists($snapfile)) {
                 $broken = true;
                 break;
             }
         }
🤖 Prompt for AI Agents
In @emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php around lines
1934 - 1951, The purge_deleted_snapshots function assumes $snaps is a non-null
array and that each $snap contains an iterable 'disks' key; add a null/type
check at the top to return early or cast to an array if $snaps is null/invalid,
and inside the foreach over $snaps ensure isset($snap['disks']) &&
is_array($snap['disks']) before iterating; for malformed entries
(missing/invalid 'disks') skip/unset them, and only access
$disk['source']['@attributes']['file'] after verifying those keys exist before
calling file_exists.

Comment on lines +1960 to +1963
// Only destructive operations may invalidate snapshots
if ($delete_used) {
purge_deleted_snapshots($snaps);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard against null $snaps before calling purge function.

If the snapshots database file doesn't exist or contains invalid JSON, json_decode() returns null. Passing null to purge_deleted_snapshots() will fail the array type hint.

🛠️ Suggested fix
     // Only destructive operations may invalidate snapshots
-    if ($delete_used) {
+    if ($delete_used && is_array($snaps)) {
         purge_deleted_snapshots($snaps);
     }
🤖 Prompt for AI Agents
In @emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php around lines
1960 - 1963, Guard against null $snaps before calling purge_deleted_snapshots():
when json_decode() can return null, wrap the call so it only runs if
$delete_used is true AND $snaps is an array (e.g. if ($delete_used &&
is_array($snaps)) purge_deleted_snapshots($snaps);), or explicitly
convert/validate $snaps to an array before calling purge_deleted_snapshots() so
the function's array type hint is never passed null.

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