Skip to content

7.x Various VM Updates#1988

Merged
limetech merged 13 commits into
unraid:masterfrom
SimonFair:vmdashusage-fix-
Feb 6, 2025
Merged

7.x Various VM Updates#1988
limetech merged 13 commits into
unraid:masterfrom
SimonFair:vmdashusage-fix-

Conversation

@SimonFair

@SimonFair SimonFair commented Jan 14, 2025

Copy link
Copy Markdown
Contributor
  1. Add support for Virgl
  2. Add additional Support for Templates to save, restore, download and upload.
  3. Display Heads and VRAM options
  4. Add Custom CPUs Backend functions

Summary by CodeRabbit

  • New Features

    • The "Add VM/Templates" button now clearly indicates its dual purpose for adding virtual machines and templates.
    • Template management has been enhanced with options to save, import, export, and download configuration data.
    • Virtual machine configuration now offers improved GPU and display options with dynamic feedback and warnings.
  • Style

    • Updated styling for file tree components across various themes delivers a more consistent and polished user interface.

@SimonFair SimonFair marked this pull request as draft January 14, 2025 21:05
@SimonFair SimonFair changed the title Various VM Updates 7.x Various VM Updates Jan 14, 2025
@coderabbitai

coderabbitai Bot commented Jan 29, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This pull request updates several components of the virtual machine manager plugin. The changes include an updated label on the VM addition button and enhanced UI functionality for managing VM templates. New JavaScript functions for saving, importing, exporting, and uploading templates have been added alongside corresponding backend cases. GPU configuration handling in the VM graphics component has been improved, and new methods in the libvirt integration refine capability retrieval and display options. Additional CSS rules have been introduced across multiple theme files, and form logic for GPU and CPU configuration has been updated.

Changes

File(s) Change Summary
emhttp/plugins/dynamix.vm.manager/VMMachines.page Updated the "Add VM" button label to "Add VM/Templates".
emhttp/plugins/dynamix.vm.manager/VMTemplates.page Added new UI elements and JavaScript functions for saving, importing, exporting, and uploading templates; enhanced dialog management and hover effects.
emhttp/plugins/dynamix.vm.manager/include/VMMachines.php Introduced GPU configuration logic to append GPU driver and render details to the VM graphics output.
emhttp/plugins/dynamix.vm.manager/include/VMajax.php Added new switch cases (vm-template-save, vm-template-import) for handling template save and import operations with file existence and replacement checks.
emhttp/plugins/dynamix.vm.manager/include/libvirt.php Added get_domain_capabilities and new methods (domain_get_vnc_render, domain_get_vnc_display_options, domain_get_cpu_custom); refined XPath queries in existing methods for VNC and VMRC data.
emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php Introduced a new display options array and a QXL display model; modified array_update_recursive to always preserve graphics device configuration.
emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates-{azure,black,gray,white}.css
emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates.css
Added a new .fileTree CSS class across multiple theme files with fixed dimensions, background colors, and scroll settings; also added a rule hiding specific elements (div.template, div#dialogWindow, and input#upload).
emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php Enhanced VM configuration forms by adding new GPU keys (render, DisplayOptions) and updating CPU options and the VMConsoleDriverChange function for dynamic UI adjustments based on user selections.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant UI as VMTemplates UI
    participant JS as Template JS Functions
    participant API as VMajax Endpoint

    U->>UI: Click "Save/Import Template"
    UI->>JS: Trigger save/import function
    JS->>API: Send AJAX request with template data
    API-->>JS: Return success/error response
    JS-->>UI: Update template list & notify user
Loading
sequenceDiagram
    participant U as User
    participant Form as Configuration Form
    participant Func as VMConsoleDriverChange

    U->>Form: Change VNC model selection
    Form->>Func: Trigger driver change function
    Func-->>Form: Adjust visibility of render & display option fields
Loading

Poem

I'm a bunny hopping through lines of code,
Discovering new templates along every road.
Buttons now whisper dual tasks to display,
GPU details and styles brighten my day.
With each update, I skip in delight,
Coding carrots keep my world so light!

Tip

🌐 Web search-backed reviews and chat
  • We have enabled web search-based reviews and chat for all users. This feature allows CodeRabbit to access the latest documentation and information on the web.
  • You can disable this feature by setting web_search: false in the knowledge_base settings.
  • Please share any feedback in the Discord discussion.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@SimonFair SimonFair marked this pull request as ready for review February 1, 2025 15:27
@SimonFair

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Feb 1, 2025

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (9)
emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php (4)

105-107: LGTM! Consider adding documentation for the new GPU configuration options.

The new GPU configuration options for render device and display settings are well implemented. Consider adding comments to document:

  • The purpose and valid values for the 'render' field
  • The format and options for 'DisplayOptions'

469-471: Consider improving accessibility for CPU migration controls.

While the hidden attribute works correctly for visibility control, consider:

  1. Using ARIA attributes to improve screen reader support
  2. Adding a tooltip to explain when/why the migration option is hidden

1164-1165: Enhance GPU boot warning with more specific guidance.

The warning about vBIOS requirements for primary GPUs is helpful, but consider:

  1. Adding a link to documentation about obtaining vBIOS files
  2. Specifying which GPU models typically require vBIOS
  3. Providing steps to verify if vBIOS is needed

Also applies to: 1299-1301, 1380-1380


2115-2138: Consider refactoring VMConsoleDriverChange for better maintainability.

While the function works correctly, consider:

  1. Using constants for driver types ('virtio3d', 'qxl')
  2. Extracting repeated DOM manipulation into helper functions
  3. Using CSS classes instead of direct style manipulation

Example refactor:

function VMConsoleDriverChange(driver) {
  const VIRTIO3D = 'virtio3d';
  const QXL = 'qxl';
  
  const toggleElements = (elements, show) => {
    elements.forEach(el => {
      el.style.display = show ? 'inline' : 'none';
      el.style.visibility = show ? 'visible' : 'hidden';
    });
  };
  
  const renderElements = ['vncrender', 'vncrendertext'].map(id => document.getElementById(id));
  const displayElements = ['vncdspopt', 'vncdspopttext'].map(id => document.getElementById(id));
  
  toggleElements(renderElements, driver.value === VIRTIO3D);
  toggleElements(displayElements, driver.value === QXL);
}
emhttp/plugins/dynamix.vm.manager/include/libvirt.php (3)

2245-2256: Consider extracting render node path handling to a helper function.

The render node path manipulation logic could be encapsulated in a separate helper function for better maintainability.

-			if (!str_contains($var,"pci")) $var = trim(shell_exec("udevadm info -q symlink  -r $var"));
-			$var = str_replace(['/dev/dri/by-path/pci-0000:','-render'],['',''],$var);
+			private function normalize_render_node_path($path) {
+				if (!str_contains($path, "pci")) {
+					$path = trim(shell_exec("udevadm info -q symlink -r $path"));
+				}
+				return str_replace(['/dev/dri/by-path/pci-0000:', '-render'], ['', ''], $path);
+			}

2355-2385: Add input validation for the domain parameter.

The method correctly retrieves CPU configuration attributes but should validate the domain parameter.

 function domain_get_cpu_custom($domain) {
+			if (!$domain) {
+				return $this->_set_last_error();
+			}
+			$domain = $this->get_domain_object($domain);
+			if (!$domain) {
+				return $this->_set_last_error();
+			}
+
 			$tmp = $this->get_xpath($domain, '//domain/cpu/@match', false);

872-889: Consider using constants for default display values.

The implementation correctly handles virtio3d and display options, but could benefit from defined constants.

+			private const DEFAULT_QXL_DISPLAY_OPTIONS = "ram='65536' vram='16384' vgamem='16384' heads='1' primary='yes'";
+
 			$strEGLHeadless = "";
 			$strAccel3d ="";
 			if ($strModelType == "virtio3d") {
 				$strModelType = "virtio";
 				if (!isset($gpu['render'])) $gpu['render'] = "auto";
 				if ($gpu['render'] == "auto") {
 					$strEGLHeadless = '<graphics type="egl-headless"><gl enable="yes"/></graphics>';
 					$strAccel3d = "<acceleration accel3d='yes'/>";
 				} else {
 					$strEGLHeadless = '<graphics type="egl-headless"><gl enable="yes" rendernode="/dev/dri/by-path/pci-0000:'.$gpu['render'].'-render"/></graphics>';
 					$strAccel3d ="<acceleration accel3d='yes'/>";
 			}}

 			$strDisplayOptions = "";
 			if ($strModelType == "qxl") {
-				if (empty($gpu['DisplayOptions'])) $gpu['DisplayOptions'] ="ram='65536' vram='16384' vgamem='16384' heads='1' primary='yes'";
+				if (empty($gpu['DisplayOptions'])) $gpu['DisplayOptions'] = self::DEFAULT_QXL_DISPLAY_OPTIONS;
 				$strDisplayOptions = $gpu['DisplayOptions'];
 			}
emhttp/plugins/dynamix.vm.manager/VMTemplates.page (2)

111-129: Add cleanup for failed downloads.

The downloadJSON function should handle failed downloads and clean up temporary resources.

Apply this diff to add cleanup:

 function downloadJSON(data, filename = 'data.json') {
+    let url;
     try {
         // Convert the data to a JSON string
         const jsonString = JSON.stringify(data, null, 2); // Beautify with 2 spaces
         // Create a Blob from the JSON string
         const blob = new Blob([jsonString], { type: 'application/json' });
         // Generate a temporary URL for the Blob
-        const url = URL.createObjectURL(blob);
+        url = URL.createObjectURL(blob);
         
         // Create a link element
         const link = document.createElement('a');
         link.href = url;
         link.download = filename; // Specify the file name
         
         // Trigger the download by programmatically clicking the link
         link.click();
+    } catch (e) {
+        console.error('Failed to download JSON:', e);
     } finally {
         // Clean up by revoking the object URL
-        URL.revokeObjectURL(url);
+        if (url) URL.revokeObjectURL(url);
     }
 }

250-267: Add input validation for template names.

The dialog templates should include validation for template names to prevent invalid characters.

Add pattern validation to the input fields:

-<input type="text" id="target" autocomplete="off" spellcheck="false" value="" data-pickcloseonfile="true" data-pickfolders="true" data-pickfilter="iso">
+<input type="text" id="target" autocomplete="off" spellcheck="false" value="" pattern="[a-zA-Z0-9_-]+" title="Only alphanumeric characters, hyphens and underscores are allowed" data-pickcloseonfile="true" data-pickfolders="true" data-pickfilter="iso">
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8e1d85 and a8c4417.

📒 Files selected for processing (12)
  • emhttp/plugins/dynamix.vm.manager/VMMachines.page (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/VMTemplates.page (3 hunks)
  • emhttp/plugins/dynamix.vm.manager/include/VMMachines.php (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/include/VMajax.php (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/include/libvirt.php (7 hunks)
  • emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php (6 hunks)
  • emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates-azure.css (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates-black.css (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates-gray.css (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates-white.css (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates.css (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php (11 hunks)
✅ Files skipped from review due to trivial changes (5)
  • emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates.css
  • emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates-white.css
  • emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates-gray.css
  • emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates-black.css
  • emhttp/plugins/dynamix.vm.manager/sheets/VMTemplates-azure.css
🔇 Additional comments (12)
emhttp/plugins/dynamix.vm.manager/include/VMMachines.php (2)

81-84: LGTM! GPU driver and render information display is well implemented.

The code properly handles:

  • GPU model detection and driver info display
  • Special case for virtio3d with render settings
  • Auto render mode vs specific GPU device selection

91-91: LGTM! Graphics display string construction is consistent.

The code maintains a consistent format by:

  • Appending GPU driver info to existing protocol info
  • Preserving auto-connection status display

Also applies to: 96-96

emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php (5)

711-711: LGTM! QXL model definition is well-specified.

The model definition includes all necessary parameters:

  • RAM and VRAM sizes
  • Video memory size
  • Number of heads
  • Primary display flag

713-758: LGTM! Display options array provides comprehensive configuration choices.

The array is well-structured with:

  • Clear text descriptions for each option
  • Consistent XML attribute patterns
  • Good coverage of memory sizes (16M to 256M)
  • Support for multiple displays (1, 2, 4 heads)

918-925: LGTM! Boot VGA detection is properly implemented.

The code:

  • Correctly checks device class before reading boot_vga
  • Safely handles file read operations
  • Properly trims the read value

941-941: LGTM! Boot VGA field addition is consistent with device info structure.

The bootvga field is properly added to the device info array.


1546-1546: LGTM! Graphics device handling is simplified.

Removal of old graphics device unset logic improves the code by:

  • Eliminating unnecessary state manipulation
  • Maintaining graphics settings consistency
emhttp/plugins/dynamix.vm.manager/include/libvirt.php (2)

58-71: LGTM! Well-documented domain capabilities implementation.

The method is properly implemented with clear parameter documentation and consistent error handling.


2205-2206: LGTM! Improved graphics device type filtering.

The XPath query now correctly filters for both spice and vnc graphics types.

emhttp/plugins/dynamix.vm.manager/VMTemplates.page (2)

41-50: LGTM! Dependencies are correctly included.

The required CSS and JavaScript files are properly included for jQuery UI components, switch buttons, and file trees.


59-61: LGTM! Template management buttons are well-organized.

The buttons for canceling, importing from file, and uploading are properly aligned and centered.

emhttp/plugins/dynamix.vm.manager/VMMachines.page (1)

489-489: LGTM! Button text accurately reflects dual functionality.

The button text "Add VM/Templates" correctly indicates that users can add both VMs and templates.

Comment on lines +209 to +225
function uploadFile(files,index,start,time) {
var file = files[0];
var blob = file;
reader.onloadend = function(e){
uploadedData = JSON.parse(e.target.result);
//alert('File uploaded successfully. Click "Process JSON" to proceed.');
saveUserTemplateImport(file.name,uploadedData);
};
reader.readAsText(blob);
}

function startUpload(files) {
if (files.length==0) return;
reader = new FileReader();
//window.onbeforeunload = function(e){return '';};
uploadFile(files);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add file type validation for uploads.

The file upload functions should validate the file type before processing.

Apply this diff to add validation:

 function uploadFile(files,index,start,time) {
   var file = files[0];
+  // Validate file type
+  if (!file.name.toLowerCase().endsWith('.json')) {
+    swal({title:"_(Error occurred)_?",text:"Invalid file type. Only JSON files are allowed.",type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
+    return;
+  }
   var blob = file;
   reader.onloadend = function(e){
+    try {
       uploadedData = JSON.parse(e.target.result);
+    } catch (e) {
+      swal({title:"_(Error occurred)_?",text:"Invalid JSON format: " + e.message,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
+      return;
+    }
       saveUserTemplateImport(file.name,uploadedData);
   };
   reader.readAsText(blob);     
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function uploadFile(files,index,start,time) {
var file = files[0];
var blob = file;
reader.onloadend = function(e){
uploadedData = JSON.parse(e.target.result);
//alert('File uploaded successfully. Click "Process JSON" to proceed.');
saveUserTemplateImport(file.name,uploadedData);
};
reader.readAsText(blob);
}
function startUpload(files) {
if (files.length==0) return;
reader = new FileReader();
//window.onbeforeunload = function(e){return '';};
uploadFile(files);
}
function uploadFile(files,index,start,time) {
var file = files[0];
// Validate file type
if (!file.name.toLowerCase().endsWith('.json')) {
swal({title:"_(Error occurred)_?", text:"Invalid file type. Only JSON files are allowed.", type:'error', html:true, showCancelButton:true, confirmButtonText:"_(OK)_"});
return;
}
var blob = file;
reader.onloadend = function(e){
try {
uploadedData = JSON.parse(e.target.result);
} catch (e) {
swal({title:"_(Error occurred)_?", text:"Invalid JSON format: " + e.message, type:'error', html:true, showCancelButton:true, confirmButtonText:"_(OK)_"});
return;
}
saveUserTemplateImport(file.name, uploadedData);
};
reader.readAsText(blob);
}
function startUpload(files) {
if (files.length==0) return;
reader = new FileReader();
//window.onbeforeunload = function(e){return '';};
uploadFile(files);
}

Comment on lines +74 to +108
function saveUserTemplateFile(name,template) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"no"},function($return){
if ($return.success == false) {
swal({title:"_(File exists)_?",text:"Replace file: " + name ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"yes"},function($return){
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
});
}
else {
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
};
});
}
});
}

function saveUserTemplateImport(name,template) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-import',name:name,template:template,replace:"no"},function($return){
if ($return.success == false) {
swal({title:"_(File exists)_?",text:"Replace file: " + name.split(".")[0] ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"yes"},function($return){
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
if ($return.success == true) refresh();
});
}
else {
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
};
});
}
if ($return.success == true) refresh();
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for JSON parsing failures.

The template management functions need to handle JSON parsing errors when saving and importing templates.

Apply this diff to add error handling:

 function saveUserTemplateFile(name,template) {
+  try {
+    // Validate template is valid JSON
+    JSON.parse(JSON.stringify(template));
+  } catch (e) {
+    swal({title:"_(Error occurred)_?",text:"Invalid template format: " + e.message,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
+    return;
+  }
   $.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"no"},function($return){
     if ($return.success == false) {
       swal({title:"_(File exists)_?",text:"Replace file: " + name ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function saveUserTemplateFile(name,template) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"no"},function($return){
if ($return.success == false) {
swal({title:"_(File exists)_?",text:"Replace file: " + name ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"yes"},function($return){
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
});
}
else {
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
};
});
}
});
}
function saveUserTemplateImport(name,template) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-import',name:name,template:template,replace:"no"},function($return){
if ($return.success == false) {
swal({title:"_(File exists)_?",text:"Replace file: " + name.split(".")[0] ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"yes"},function($return){
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
if ($return.success == true) refresh();
});
}
else {
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
};
});
}
if ($return.success == true) refresh();
});
}
function saveUserTemplateFile(name,template) {
try {
// Validate template is valid JSON
JSON.parse(JSON.stringify(template));
} catch (e) {
swal({title:"_(Error occurred)_?", text:"Invalid template format: " + e.message, type:'error', html:true, showCancelButton:true, confirmButtonText:"_(OK)_"});
return;
}
$.post('/plugins/dynamix.vm.manager/include/VMajax.php', {action:'vm-template-save', name:name, template:template, replace:"no"}, function($return){
if ($return.success == false) {
swal({title:"_(File exists)_?", text:"Replace file: " + name, type:'warning', html:true, showCancelButton:true, confirmButtonText:"_(Proceed)_", cancelButtonText:"_(No)_"}, function(p){
if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php', {action:'vm-template-save', name:name, template:template, replace:"yes"}, function($return){
if ($return.success == false)
swal({title:"_(Error occured)_?", text:"Action error " + name + " " + $return.error, type:'warning', html:true, showCancelButton:true, confirmButtonText:"_(OK)_"});
});
}
else {
if ($return.success == false)
swal({title:"_(Error occured)_?", text:"Action error " + name + " " + $return.error, type:'error', html:true, showCancelButton:true, confirmButtonText:"_(OK)_"});
};
});
}
});
}
function saveUserTemplateImport(name,template) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php', {action:'vm-template-import', name:name, template:template, replace:"no"}, function($return){
if ($return.success == false) {
swal({title:"_(File exists)_?", text:"Replace file: " + name.split(".")[0], type:'warning', html:true, showCancelButton:true, confirmButtonText:"_(Proceed)_", cancelButtonText:"_(No)_"}, function(p){
if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php', {action:'vm-template-save', name:name, template:template, replace:"yes"}, function($return){
if ($return.success == false)
swal({title:"_(Error occured)_?", text:"Action error " + name.split(".")[0] + " " + $return.error, type:'warning', html:true, showCancelButton:true, confirmButtonText:"_(OK)_"});
if ($return.success == true) refresh();
});
}
else {
if ($return.success == false)
swal({title:"_(Error occured)_?", text:"Action error " + name + " " + $return.error, type:'error', html:true, showCancelButton:true, confirmButtonText:"_(OK)_"});
};
});
}
if ($return.success == true) refresh();
});
}

Comment on lines +871 to +886
case 'vm-template-save':
$template = $_REQUEST['template'];
$name = $_REQUEST['name'];
$replace = $_REQUEST['replace'];

if (is_file($name) && $replace == "no"){
$arrResponse = ['success' => false, 'error' => _("File exists.")];
} else {
$error = file_put_contents($name,json_encode($template));
if ($error !== false) $arrResponse = ['success' => true];
else {
$arrResponse = ['success' => false, 'error' => _("File write failed.")];
}
}
break;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation and error handling.

The template save case needs input validation and better error handling.

Apply this diff to improve the code:

 case 'vm-template-save':
+    // Validate inputs
+    if (empty($_REQUEST['template']) || empty($_REQUEST['name'])) {
+        $arrResponse = ['success' => false, 'error' => _('Missing required parameters.')];
+        break;
+    }
+    
     $template = $_REQUEST['template'];    
     $name = $_REQUEST['name'];    
     $replace = $_REQUEST['replace'];    
 
+    // Validate path
+    $dir = dirname($name);
+    if (!is_dir($dir)) {
+        $arrResponse = ['success' => false, 'error' => _('Invalid directory path.')];
+        break;
+    }
+
+    // Validate write permissions
+    if (!is_writable($dir)) {
+        $arrResponse = ['success' => false, 'error' => _('Directory is not writable.')];
+        break;
+    }
+
     if (is_file($name) && $replace == "no"){
         $arrResponse = ['success' => false, 'error' => _("File exists.")];
     } else {
-        $error = file_put_contents($name,json_encode($template));
+        // Ensure template is valid JSON
+        $json = json_encode($template, JSON_PRETTY_PRINT);
+        if ($json === false) {
+            $arrResponse = ['success' => false, 'error' => _('Invalid template format.')];
+            break;
+        }
+        
+        $error = file_put_contents($name, $json);
         if ($error !== false)  $arrResponse = ['success' => true]; 
         else {
             $arrResponse = ['success' => false, 'error' => _("File write failed.")];
         }
     }
     break;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case 'vm-template-save':
$template = $_REQUEST['template'];
$name = $_REQUEST['name'];
$replace = $_REQUEST['replace'];
if (is_file($name) && $replace == "no"){
$arrResponse = ['success' => false, 'error' => _("File exists.")];
} else {
$error = file_put_contents($name,json_encode($template));
if ($error !== false) $arrResponse = ['success' => true];
else {
$arrResponse = ['success' => false, 'error' => _("File write failed.")];
}
}
break;
case 'vm-template-save':
// Validate inputs
if (empty($_REQUEST['template']) || empty($_REQUEST['name'])) {
$arrResponse = ['success' => false, 'error' => _('Missing required parameters.')];
break;
}
$template = $_REQUEST['template'];
$name = $_REQUEST['name'];
$replace = $_REQUEST['replace'];
// Validate path
$dir = dirname($name);
if (!is_dir($dir)) {
$arrResponse = ['success' => false, 'error' => _('Invalid directory path.')];
break;
}
// Validate write permissions
if (!is_writable($dir)) {
$arrResponse = ['success' => false, 'error' => _('Directory is not writable.')];
break;
}
if (is_file($name) && $replace == "no"){
$arrResponse = ['success' => false, 'error' => _("File exists.")];
} else {
// Ensure template is valid JSON
$json = json_encode($template, JSON_PRETTY_PRINT);
if ($json === false) {
$arrResponse = ['success' => false, 'error' => _('Invalid template format.')];
break;
}
$error = file_put_contents($name, $json);
if ($error !== false) {
$arrResponse = ['success' => true];
} else {
$arrResponse = ['success' => false, 'error' => _("File write failed.")];
}
}
break;

Comment thread emhttp/plugins/dynamix.vm.manager/include/VMajax.php

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php (1)

2115-2138: Enhance error handling in VMConsoleDriverChange function.

The function should handle edge cases where elements don't exist to prevent potential JavaScript errors.

-function VMConsoleDriverChange(driver) {
+function VMConsoleDriverChange(driver) {
+  if (!driver) return;
+  
+  const vncrender = document.getElementById("vncrender");
+  const vncrendertext = document.getElementById("vncrendertext");
+  const vncdspopt = document.getElementById("vncdspopt");
+  const vncdspopttext = document.getElementById("vncdspopttext");
+  
+  if (!vncrender || !vncrendertext || !vncdspopt || !vncdspopttext) return;
+  
   if (driver.value != "virtio3d") {
-    document.getElementById("vncrender").style.visibility="hidden";
-    document.getElementById("vncrendertext").style.visibility="hidden";
-    document.getElementById("vncrender").style.display="none";
-    document.getElementById("vncrendertext").style.display="none";
+    vncrender.style.visibility = "hidden";
+    vncrender.style.display = "none";
+    vncrendertext.style.visibility = "hidden";
+    vncrendertext.style.display = "none";
   } else {
-    document.getElementById("vncrender").style.display="inline";
-    document.getElementById("vncrender").style.visibility="visible";
-    document.getElementById("vncrendertext").style.display="inline";
-    document.getElementById("vncrendertext").style.visibility="visible";
+    vncrender.style.display = "inline";
+    vncrender.style.visibility = "visible";
+    vncrendertext.style.display = "inline";
+    vncrendertext.style.visibility = "visible";
   }
   if (driver.value != "qxl") {
-    document.getElementById("vncdspopt").style.visibility="hidden";
-    document.getElementById("vncdspopttext").style.visibility="hidden";
+    vncdspopt.style.visibility = "hidden";
+    vncdspopttext.style.visibility = "hidden";
   } else {
-    document.getElementById("vncdspopt").style.display="inline";
-    document.getElementById("vncdspopt").style.visibility="visible";
-    document.getElementById("vncdspopttext").style.display="inline";
-    document.getElementById("vncdspopttext").style.visibility="visible";
+    vncdspopt.style.display = "inline";
+    vncdspopt.style.visibility = "visible";
+    vncdspopttext.style.display = "inline";
+    vncdspopttext.style.visibility = "visible";
   }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74f78fb and b395180.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php (11 hunks)
🔇 Additional comments (5)
emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php (5)

105-107: LGTM: GPU configuration array enhancement.

The addition of render and DisplayOptions fields to the GPU configuration array provides better control over GPU rendering and display settings.


469-471: LGTM: Improved CPU migration UI visibility.

The CPU migration dropdown is now properly hidden when not applicable, improving UI clarity.


1299-1301: LGTM: Added GPU boot VGA warning.

The warning about vBIOS requirement for primary GPU adapter improves user guidance.


2489-2492: LGTM: Improved GPU boot VGA visibility handling.

The code properly handles the visibility of GPU boot VGA warning based on the GPU configuration.


1254-1276: Verify the display options configuration.

The new display options for QXL and Virtio3D drivers need verification:

  1. QXL display options should match the supported configurations
  2. Virtio3D render device selection should be compatible with the selected GPU
✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Verify QXL and Virtio3D configurations in libvirt

# Check QXL display options
virsh domcapabilities --virttype kvm | grep -A 10 "<video"

# Check Virtio3D support
virsh domcapabilities --virttype kvm | grep -A 10 "virtio3d"

Length of output: 313


I'll generate a new shell script to search the codebase for references to the QXL display options (i.e. "qxlxml") and for occurrences of "virtio3d". This will help us verify whether the configurations in the PHP template match the supported settings in the codebase.


🏁 Script executed:

#!/bin/bash
echo "Searching for QXL display options configuration ('qxlxml')..."
rg -i "qxlxml"

echo ""
echo "Searching for Virtio3D references..."
rg -i "virtio3d"

Length of output: 3339


Verification of Display Options Configuration

• The QXL display options in Custom.form.php use the "qxlxml" keys defined in libvirt_helpers.php. These definitions (e.g., RAM, VRAM settings, etc.) match the supported configurations.
• The virtio3d option correctly toggles the visibility of the render device selection based on the GPU model, ensuring that the render device can be specified when virtio3d is selected.

Based on the code references and our search results, the configurations for both QXL and virtio3d appear to be consistent with the supported definitions.

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.

2 participants