Files
webgui/emhttp/plugins/dynamix/api/SysDevs.page
T
2025-11-13 16:29:40 +00:00

584 lines
17 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Menu="UNRAID-OS"
Title="System Devices"
Icon="icon-hardware"
Tag="server"
---
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<script>
$(function(){
$('#t1').load('/webGui/include/SysDevs.php',{table:'t1'});
$('#t2').load('/webGui/include/SysDevs.php',{table:'t2'});
$('#t3').load('/webGui/include/SysDevs.php',{table:'t3'});
$('#t4').load('/webGui/include/SysDevs.php',{table:'t4'});
});
function applyCfg() {
var message = "_(System Devices)_: _(A reboot is required to apply changes)_";
var string = "BIND=";
var elements = document.getElementById("vfiopci").elements;
for (var i = 0, element; element = elements[i++];) {
if (element.type === "checkbox" && element.checked === true && element.className.substring(0, 5) === "iommu")
string = string + element.value + " ";
}
string = string.trim();
if (string === "BIND=") {
string = "";
}
$.post( "/plugins/dynamix/include/update.vfio-pci-cfg.php", { cfg: string } )
.done(function(d) {
if (d==1) {
addRebootNotice(message);
document.getElementById("warning").innerHTML = "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
} else {
removeRebootNotice(message);
document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
}
$("#applycfg").attr("disabled",true);
});
}
function saveVFSettingsConfig(pciId,vd,interactive=1) {
var message = "_(System Devices)_: _(A reboot is required to apply changes)_";
var mac = document.getElementById("vfmac" + pciId).value;
var vfio = document.getElementById("vfvfio" + pciId).checked;
$.post( "/plugins/dynamix/include/update.sriov-cfg.php", { type:"sriovsettings", pciid: pciId, vd:vd, vfio:vfio, mac:mac } )
.done(function(d) {
if (d==1) {
//addRebootNotice(message);
// document.getElementById("warning").innerHTML = "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
if (interactive == 1)
swal({
title: "VF Settings",
text: "Configuration saved.",
type: "success",
timer: 3000,
showConfirmButton: false
});
} else {
//removeRebootNotice(message);
//document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
}
});
}
function saveVFsConfig(pciId,vd,interactive=1) {
var message = "_(System Devices)_: _(A reboot is required to apply changes)_";
var numvfs = document.getElementById("vf" + pciId).value;
$.post( "/plugins/dynamix/include/update.sriov-cfg.php", { type:"sriov", pciid: pciId, vd:vd, numvfs:numvfs } )
.done(function(d) {
if (d==1) {
// addRebootNotice(message);
// document.getElementById("warning").innerHTML = "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
if (interactive == 1)
swal({
title: "VFs",
text: "Configuration saved.",
type: "success",
timer: 3000,
showConfirmButton: false
});
} else {
// removeRebootNotice(message);
// document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
}
if (interactive == 1) $('#t1').load('/webGui/include/SysDevs.php', { table: 't1' });
});
}
function generateMAC(pciId) {
if (!pciId) {
swal("Error", "No PCI ID provided to generateMAC()", "error");
return;
}
// Validate PCI ID format
var pciPattern = /^([0-9a-fA-F]{4}):([0-9a-fA-F]{2}):([0-9a-fA-F]{2})\.([0-7])$/;
var match = pciId.match(pciPattern);
if (!match) {
swal("Invalid PCI ID", "Expected format: 0000:03:00.2", "error");
return;
}
// Ask for host ID using Unraid swal input
swal({
title: "Enter Host ID",
text: "Provide a 2-digit hex host ID (e.g. 01)",
type: "input",
showCancelButton: true,
closeOnConfirm: false,
inputPlaceholder: "00"
}, function(hostId) {
if (hostId === false) return; // cancelled
if (!/^[0-9a-fA-F]{1,2}$/.test(hostId)) {
swal.showInputError("Please enter 12 hex digits (00ff)");
return false;
}
// Extract PCI parts
var domain = match[1];
var bus = match[2];
var dev = match[3];
var func = match[4];
var dom_lo = ("0" + (parseInt(domain, 16) & 0xFF).toString(16)).slice(-2);
// Format: 62:<domain>:<bus>:<device>:<function>:<host>
var mac = [
"62",
dom_lo,
bus.padStart(2, "0"),
dev.padStart(2, "0"),
func.padStart(2, "0"),
hostId.padStart(2, "0")
].join(":").toUpperCase();
// Build target input ID (e.g., #vfmac0000:04:11.1)
var inputId = "vfmac" + pciId;
// Update the field if it exists
var $field = $("#" + CSS.escape(inputId));
if ($field.length) {
$field.val(mac);
}
swal({
title: "MAC",
text: "Generated successfully.",
type: "success",
timer: 3000,
showConfirmButton: false
});
});
}
function applyVFsConfig(pciId, vd, vfs) {
const message = "_(System Devices)_: _(A reboot is required to apply changes)_";
const numvfs = parseInt(document.getElementById("vf" + pciId).value, 10);
vfs = parseInt(vfs, 10);
// --- Step 1: Check for active VMs (always returns JSON) ---
$.post("/plugins/dynamix/include/apply.sriov-cfg.php", {
type: "inuse",
pciid: pciId,
pcitype: "PF"
}, null, "json") // automatically parse JSON
.done(function (data) {
console.log("Active check result:", data);
// If device is in use, show VM list and stop here
if (data.inuse === true) {
let vmListText = "";
let vmCount = Array.isArray(data.vms) ? data.vms.length : 0;
if (vmCount > 0) {
vmListText = data.vms.join("\n");
} else {
vmListText = "No VM names returned.";
}
const plural = vmCount === 1 ? "VM is" : "VMs are";
const messageText =
`The following ${plural} currently using this device:\n\n` +
vmListText +
"\n\nPlease stop " +
(vmCount === 1 ? "it" : "them") +
" before applying changes.";
swal({
title: "Active VMs Detected",
text: messageText,
type: "warning",
confirmButtonText: "OK",
closeOnConfirm: true
});
return;
}
// --- Step 2: Continue only if NOT in use ---
// Case 1: VFs will be removed
if (vfs !== 0 && numvfs === 0) {
swal({
title: "VFs will be removed",
text: "Card will reset.",
type: "warning",
showCancelButton: true,
confirmButtonText: "OK",
cancelButtonText: "Cancel",
closeOnConfirm: true
}, function(isConfirm) {
if (isConfirm) {
setTimeout(function() {
doVFApply(pciId, vd, numvfs, message);
}, 300);
} else {
swal("Cancelled", "No changes were made.", "info");
}
});
return;
}
// Case 2: Number of VFs changed
if (vfs !== numvfs && vfs !== 0) {
swal({
title: "Number of VFs changed",
text: "Will need to remove and re-add VFs.",
type: "warning",
showCancelButton: true,
confirmButtonText: "OK",
cancelButtonText: "Cancel",
closeOnConfirm: true
}, function(isConfirm) {
if (isConfirm) {
setTimeout(function() {
doVFApply(pciId, vd, numvfs, message);
}, 300);
} else {
swal("Cancelled", "No changes were made.", "info");
}
});
return;
}
// Case 3: All VFs removed
if (vfs !== numvfs && vfs === 0) {
doVFApply(pciId, vd, numvfs, message);
}
})
.fail(function (xhr, status, error) {
console.error("Active check failed:", status, error, xhr.responseText);
swal({
title: "Error",
text: "Could not verify active device state.",
type: "error",
showConfirmButton: true
});
});
}
function applyVFSettings(pciId, vd, currentvfio, currentmac) {
const message = "_(System Devices)_: _(A reboot is required to apply changes)_";
const mac = document.getElementById("vfmac" + pciId).value;
const vfio = document.getElementById("vfvfio" + pciId).checked;
//vfs = parseInt(vfs, 10);
// --- Step 1: Check for active VMs (always returns JSON) ---
$.post("/plugins/dynamix/include/apply.sriov-cfg.php", {
type: "inuse",
pciid: pciId,
pcitype: "VF"
}, null, "json") // automatically parse JSON
.done(function (data) {
console.log("Active check result:", data);
// If device is in use, show VM list and stop here
if (data.inuse === true) {
let vmListText = "";
let vmCount = Array.isArray(data.vms) ? data.vms.length : 0;
if (vmCount > 0) {
vmListText = data.vms.join("\n");
} else {
vmListText = "No VM names returned.";
}
const plural = vmCount === 1 ? "VM is" : "VMs are";
const messageText =
`The following ${plural} currently using this device:\n\n` +
vmListText +
"\n\nPlease stop " +
(vmCount === 1 ? "it" : "them") +
" before applying changes.";
swal({
title: "Active VMs Detected",
text: messageText,
type: "warning",
confirmButtonText: "OK",
closeOnConfirm: true
});
return;
}
doVFSettingApply(pciId, vd, vfio, mac, currentvfio,currentmac,message);
})
.fail(function (xhr, status, error) {
console.error("Active check failed:", status, error, xhr.responseText);
swal({
title: "Error",
text: "Could not verify active device state.",
type: "error",
showConfirmButton: true
});
});
}
function doVFSettingApply(pciId, vd, vfio, mac, currentvfio, currentmac, message) {
// Show "updating" alert
swal({
title: "Updating...",
text: "Please wait while configuration is applied.",
type: "info",
showConfirmButton: false,
allowOutsideClick: false,
closeOnConfirm: false
});
// Perform the POST
$.post("/plugins/dynamix/include/apply.sriov-cfg.php", {
type: "sriovsettings",
pciid: pciId,
vd: vd,
vfio: vfio,
mac: mac,
currentvfio: currentvfio,
currentmac: currentmac
})
.done(function (response) {
let data;
// Try to parse JSON safely
try {
data = (typeof response === "string") ? JSON.parse(response) : response;
} catch (e) {
swal({
title: "Error",
text: "Invalid JSON response from server.",
type: "error",
showConfirmButton: true
});
return;
}
// Handle structured PHP result
if (data.success) {
//addRebootNotice(message);
// document.getElementById("warning").innerHTML =
// "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
saveVFSettingsConfig(pciId, vd, 0);
swal({
title: "Update Complete",
text: data.details ? data.details.join("\n") : "Configuration successfully applied.",
type: "success",
timer: 3000,
showConfirmButton: false
});
} else {
//removeRebootNotice(message);
// const errorMsg = data.error || "No changes detected or operation failed.";
document.getElementById("warning").innerHTML = "<b>" + errorMsg + "</b>";
swal({
title: "Operation Failed",
text: errorMsg,
type: "error",
showConfirmButton: true
});
}
// Always reload the table after handling
$('#t1').load('/webGui/include/SysDevs.php', { table: 't1' });
})
.fail(function (xhr, status, error) {
swal({
title: "Network Error",
text: "Failed to communicate with the server.\n" + (error || status),
type: "error",
showConfirmButton: true
});
});
}
// Helper: safely stringify nested structures
function formatDetails(details) {
if (details == null) return "";
if (typeof details === "string") return details;
if (Array.isArray(details))
return details.map(formatDetails).join("\n");
if (typeof details === "object")
return Object.entries(details)
.map(([k, v]) => `${k}: ${formatDetails(v)}`)
.join("\n\n");
return String(details);
}
function doVFApply(pciId, vd, numvfs, message) {
// Show "updating" alert
swal({
title: "Updating...",
text: "Please wait while configuration is applied.",
type: "info",
showConfirmButton: false,
allowOutsideClick: false,
closeOnConfirm: false
});
// Perform the POST
$.post("/plugins/dynamix/include/apply.sriov-cfg.php", {
type: "sriov",
pciid: pciId,
vd: vd,
numvfs: numvfs
})
.done(function (data) {
// Normalize to JSON
if (typeof data === "string") {
try { data = JSON.parse(data); }
catch (e) {
data = { success: false, error: "Invalid JSON: " + e.message };
}
}
// Extract a clean message
//let msg = "Configuration successfully applied.";
if (data.error) {
msg = "Error: " + data.error;
} else if (data.details) {
if (Array.isArray(data.details)) {
msg = data.details.join("\n");
} else if (typeof data.details === "object") {
msg = Object.entries(data.details)
.map(([k, v]) => `${k}: ${JSON.stringify(v, null, 2)}`)
.join("\n\n");
} else if (typeof data.details === "string") {
msg = data.details;
}
}
// Build the message
let msg = "";
if (data.error) msg = "Error: " + data.error;
else if (data.details) msg = formatDetails(data.details);
else msg = "Configuration successfully applied.";
// Show alert
const ok = data.success === true || data.success === 1;
saveVFsConfig(pciId, vd, 0);
swal({
title: ok ? "Update Complete" : "Update Failed",
text: msg,
type: ok ? "success" : "error",
timer: ok ? 3000 : null,
showConfirmButton: !ok
});
// Reload table
$('#t1').load('/webGui/include/SysDevs.php', { table: 't1' });
})
.fail(function (xhr, status, error) {
swal({
title: "Network Error",
text: "Failed to communicate with the server.\n" + (error || status),
type: "error",
showConfirmButton: true
});
});
}
function formatFullInput(input) {
return input
.split(';')
.filter(Boolean) // remove empty trailing entry
.map(entry => {
let [pci, status] = entry.split(',');
status = status.charAt(0).toUpperCase() + status.slice(1).toLowerCase();
return `${pci} _(${status})_`;
})
.join('<br>');
}
function formatVMS(input) {
return input
.split(';')
.filter(Boolean) // remove empty trailing entry
.join('<br>');
}
function ackPCI(pcidevice, action) {
$.post('/webGui/include/PCIUpdate.php', { action: "getvm", pciid: pcidevice }).done(function(vms) {
let swaltext = "";
switch(action) {
case 'removed':
swaltext = "_(Acknowledge removal of PCI Address)_: " + pcidevice + "<br>_(VMs where PCI device used)_<br>" + formatVMS(vms);
break;
case 'changed':
swaltext = "_(Acknowledge update of PCI Address)_: " + pcidevice + "<br>_(VMs where PCI device used)_<br>"+ formatVMS(vms);
break;
case 'added':
swaltext = "_(Acknowledge addition of PCI Address)_: " + pcidevice + "<br>_(VMs where PCI device used)_<br>" + formatVMS(vms);
break;
case 'all':
swaltext = "_(Acknowledge all PCI Address modifications)_: <br>" + formatFullInput(pcidevice) + "<br>_(VMs where PCI device used)_<br>" + formatVMS(vms);
break;
}
swal({
title: "Are you sure?",
text: swaltext,
type: "warning",
html: true,
showCancelButton: true
}, function() {
$.post('/webGui/include/PCIUpdate.php', { action: action, pciid: pcidevice }).done(function(d) {
if (d === "OK") {
$('#t1').load('/webGui/include/SysDevs.php', { table: 't1' });
}
});
});
});
}
</script>
<?exec('ls /sys/kernel/iommu_groups/',$groups); ?>
<?if (count($groups) > 0):?>
**_(PCI Devices and IOMMU Groups)_**
<?else:?>
**_(PCI Devices (No IOMMU Groups Available))_**
<?endif;?>
:sysdevs_iommu_groups_help:
<?if (strpos(file_get_contents('/proc/cmdline'), 'pcie_acs_override=') !== false):?>
<p class="notice" style="line-height:30px;height:auto">_(Warning)_: _(Your system has booted with the PCIe ACS Override setting enabled)_. _(The below list doesn't not reflect the way IOMMU would naturally group devices)_.<br>
<?=my_hyperlink(_("To see natural IOMMU groups for your hardware, go to the [VM Manager] page and set the **PCIe ACS override** setting to **Disabled**"),'/Settings/VMSettings')?>.</p>
<?endif;?>
<pre><form id="vfiopci" class="js-confirm-leave" onsubmit="return false"><table id='t1' class='pre'><tr><td><div class="spinner"></div></td></tr></table></form></pre><br>
**_(CPU Thread Pairings)_**
:sysdevs_thread_pairings_help:
<pre><table id='t2' class='pre'><tr><td><div class="spinner"></div></td></tr></table></pre><br>
**_(USB Devices)_**
:sysdevs_usb_devices_help:
<pre><table id='t3' class='pre'><tr><td><div class="spinner"></div></td></tr></table></pre><br>
**_(SCSI Devices)_**
:sysdevs_scsi_devices_help:
<pre><table id='t4' class='pre'><tr><td><div class="spinner"></div></td></tr></table></pre>
<input type="button" value="_(Done)_" onclick="done()">