mirror of
https://github.com/unraid/webgui.git
synced 2026-01-06 09:39:58 -06:00
Update WIP
This commit is contained in:
@@ -55,19 +55,19 @@ function saveVFSettingsConfig(pciId,vd,interactive=1) {
|
||||
$.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>";
|
||||
//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.",
|
||||
title: _("VF Settings"),
|
||||
text: _("Configuration saved") + ".",
|
||||
type: "success",
|
||||
timer: 3000,
|
||||
showConfirmButton: false
|
||||
});
|
||||
} else {
|
||||
removeRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
|
||||
//removeRebootNotice(message);
|
||||
//document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -78,19 +78,19 @@ function saveVFsConfig(pciId,vd,interactive=1) {
|
||||
$.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>";
|
||||
// addRebootNotice(message);
|
||||
// document.getElementById("warning").innerHTML = "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
|
||||
if (interactive == 1)
|
||||
swal({
|
||||
title: "VFs",
|
||||
text: "Configuration saved.",
|
||||
text: _("Configuration saved") + ".",
|
||||
type: "success",
|
||||
timer: 3000,
|
||||
showConfirmButton: false
|
||||
});
|
||||
} else {
|
||||
removeRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
|
||||
// removeRebootNotice(message);
|
||||
// document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
|
||||
}
|
||||
if (interactive == 1) $('#t1').load('/webGui/include/SysDevs.php', { table: 't1' });
|
||||
});
|
||||
@@ -99,7 +99,7 @@ function saveVFsConfig(pciId,vd,interactive=1) {
|
||||
|
||||
function generateMAC(pciId) {
|
||||
if (!pciId) {
|
||||
swal("Error", "No PCI ID provided to generateMAC()", "error");
|
||||
swal("Error", _("No PCI ID provided to generateMAC()"), "error");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ function generateMAC(pciId) {
|
||||
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");
|
||||
swal(_("Invalid PCI ID"), _("Expected format: 0000:03:00.2"), "error");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -314,11 +314,7 @@ function applyVFSettings(pciId, vd, currentvfio, currentmac) {
|
||||
|
||||
return;
|
||||
}
|
||||
saveVFSettingsConfig(pciId, vd, 0);
|
||||
|
||||
|
||||
doVFSettingApply(pciId, vd, vfio, mac, currentvfio,currentmac,message);
|
||||
|
||||
doVFSettingApply(pciId, vd, vfio, mac, currentvfio,currentmac,message);
|
||||
})
|
||||
.fail(function (xhr, status, error) {
|
||||
console.error("Active check failed:", status, error, xhr.responseText);
|
||||
@@ -370,9 +366,9 @@ function doVFSettingApply(pciId, vd, vfio, mac, currentvfio, currentmac, message
|
||||
|
||||
// Handle structured PHP result
|
||||
if (data.success) {
|
||||
addRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML =
|
||||
"<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
|
||||
//addRebootNotice(message);
|
||||
// document.getElementById("warning").innerHTML =
|
||||
// "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
|
||||
saveVFSettingsConfig(pciId, vd, 0);
|
||||
swal({
|
||||
title: "Update Complete",
|
||||
@@ -382,8 +378,8 @@ function doVFSettingApply(pciId, vd, vfio, mac, currentvfio, currentmac, message
|
||||
showConfirmButton: false
|
||||
});
|
||||
} else {
|
||||
removeRebootNotice(message);
|
||||
const errorMsg = data.error || "No changes detected or operation failed.";
|
||||
//removeRebootNotice(message);
|
||||
// const errorMsg = data.error || "No changes detected or operation failed.";
|
||||
document.getElementById("warning").innerHTML = "<b>" + errorMsg + "</b>";
|
||||
|
||||
swal({
|
||||
@@ -407,6 +403,18 @@ function doVFSettingApply(pciId, vd, vfio, mac, currentvfio, currentmac, message
|
||||
});
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -426,55 +434,73 @@ function doVFApply(pciId, vd, numvfs, message) {
|
||||
vd: vd,
|
||||
numvfs: numvfs
|
||||
})
|
||||
.done(function (data) {
|
||||
// Normalize to JSON object
|
||||
if (typeof data === "string") {
|
||||
try { data = JSON.parse(data); }
|
||||
catch (e) {
|
||||
data = { success: false, error: "Invalid JSON: " + e.message };
|
||||
.done(function (data) {
|
||||
// Parse JSON safely
|
||||
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.";
|
||||
// Helper: recursively format details to plain text
|
||||
function formatDetails(obj, indent = 0) {
|
||||
const pad = " ".repeat(indent);
|
||||
if (obj == null) return "";
|
||||
if (typeof obj === "string") return pad + obj;
|
||||
if (typeof obj === "number" || typeof obj === "boolean") return pad + obj;
|
||||
if (Array.isArray(obj)) return obj.map(x => formatDetails(x, indent)).join("\n");
|
||||
if (typeof obj === "object") {
|
||||
return Object.entries(obj).map(([key, val]) => {
|
||||
if (typeof val === "object" && val !== null) {
|
||||
// Special case for VF result objects
|
||||
if ("success" in val || "error" in val || "details" in val) {
|
||||
const symbol = val.success ? "✔" : "✖";
|
||||
const msg = val.error || val.details || "";
|
||||
return `${pad}${key}\n${pad} ${symbol} ${msg}`;
|
||||
}
|
||||
return `${pad}${key}:\n${formatDetails(val, indent + 1)}`;
|
||||
}
|
||||
return `${pad}${key}: ${val}`;
|
||||
}).join("\n");
|
||||
}
|
||||
return pad + String(obj);
|
||||
}
|
||||
|
||||
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 message
|
||||
let msg = "";
|
||||
if (data.error) {
|
||||
msg = "Error: " + data.error;
|
||||
} else if (data.details) {
|
||||
msg = formatDetails(data.details);
|
||||
} else {
|
||||
msg = "Configuration successfully applied.";
|
||||
}
|
||||
|
||||
// Pick alert type
|
||||
// Show alert
|
||||
const ok = data.success === true || data.success === 1;
|
||||
saveVFsConfig(pciId, vd, 0);
|
||||
// Show success or failure alert
|
||||
swal({
|
||||
title: data.success ? "Update Complete" : "Update Failed",
|
||||
text: msg,
|
||||
type: data.success ? "success" : "error",
|
||||
timer: data.success ? 3000 : null,
|
||||
showConfirmButton: !data.success
|
||||
});
|
||||
|
||||
// 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
|
||||
});
|
||||
});
|
||||
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
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
583
emhttp/plugins/dynamix/api/SysDevs.page
Normal file
583
emhttp/plugins/dynamix/api/SysDevs.page
Normal file
@@ -0,0 +1,583 @@
|
||||
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 1–2 hex digits (00–ff)");
|
||||
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()">
|
||||
@@ -13,16 +13,11 @@
|
||||
?>
|
||||
<?
|
||||
|
||||
|
||||
#$allowedPCIClass = ['0x02','0x03'];
|
||||
$allowedPCIClass = ['0x02'];
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt.php";
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get available kernel modules for this PCI device based on its modalias
|
||||
*/
|
||||
@@ -35,7 +30,6 @@ function getModulesFromModalias(string $pci): array {
|
||||
return $out ? preg_split('/\s+/', $out) : [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enumerate SR-IOV capable PCI devices (keyed by PCI address).
|
||||
*
|
||||
@@ -150,14 +144,14 @@ function rebindVfDriver($vf, $sriov, $target = 'original')
|
||||
$res = ['pci'=>$vf,'success'=>false,'error'=>null,'details'=>[]];
|
||||
$vf_path = "/sys/bus/pci/devices/$vf";
|
||||
$physfn = "$vf_path/physfn";
|
||||
if (!is_link($physfn)) return $res + ['error'=>"Missing physfn link"];
|
||||
if (!is_link($physfn)) return $res + ['error'=>_("Missing physfn link")];
|
||||
$pf = basename(readlink($physfn));
|
||||
$vf_info = $sriov[$pf]['vfs'][$vf] ?? null;
|
||||
if (!$vf_info) return $res + ['error'=>"VF not found in \$sriov for PF $pf"];
|
||||
if (!$vf_info) return $res + ['error'=>_("VF not found in sriov for PF")." $pf"];
|
||||
|
||||
$orig_mod = $vf_info['modules'][0] ?? $sriov[$pf]['module'] ?? null;
|
||||
$curr_drv = $vf_info['driver'] ?? null;
|
||||
if (!$orig_mod) return $res + ['error'=>"No module info for $vf"];
|
||||
if (!$orig_mod) return $res + ['error'=>_("No module info for")." $vf"];
|
||||
|
||||
$drv_override = "$vf_path/driver_override";
|
||||
|
||||
@@ -187,13 +181,12 @@ function rebindVfDriver($vf, $sriov, $target = 'original')
|
||||
$bound = basename(readlink($drv_link));
|
||||
if ($bound === $new_drv) {
|
||||
$res['success'] = true;
|
||||
$res['details'][] = "Bound to $new_drv";
|
||||
$res['details'][] = _("Bound to")." $new_drv";
|
||||
return $res;
|
||||
}
|
||||
return $res + ['error'=>"Bound to $bound instead of $new_drv"];
|
||||
return $res + ['error'=> sprintf(_("Bound to %s instead of %s"),$bound,$new_drv)];
|
||||
}
|
||||
|
||||
return $res + ['error'=>"No driver link after reprobe"];
|
||||
return $res + ['error'=>_("No driver link after reprobe")];
|
||||
}
|
||||
|
||||
|
||||
@@ -268,7 +261,7 @@ function getVfListByIommuGroup(): array {
|
||||
$iommu_group = "unknown";
|
||||
}
|
||||
|
||||
$groups[] = "IOMMU group " . $iommu_group;
|
||||
$groups[] = "IOMMU group " . $iommu_group;
|
||||
$groups[] = $vf_pci;
|
||||
}
|
||||
|
||||
@@ -280,28 +273,26 @@ function getVfListByIommuGroup(): array {
|
||||
// Parse SR-IOV VF counts
|
||||
// ----------------------
|
||||
function parseVFvalues() {
|
||||
$sriov_devices = [];
|
||||
$DBDF_SRIOV_REGEX = '/^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}\.[[:xdigit:]]\|[[:xdigit:]]{4}:[[:xdigit:]]{4}\|[[:digit:]]+$/';
|
||||
if (is_file("/boot/config/sriov.cfg")) {
|
||||
$file = trim(file_get_contents("/boot/config/sriov.cfg"));
|
||||
$file = preg_replace('/^VFS=/', '', $file); // Remove prefix
|
||||
$entries = preg_split('/\s+/', $file, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$sriov_devices = [];
|
||||
$DBDF_SRIOV_REGEX = '/^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}\.[[:xdigit:]]\|[[:xdigit:]]{4}:[[:xdigit:]]{4}\|[[:digit:]]+$/';
|
||||
if (is_file("/boot/config/sriov.cfg")) {
|
||||
$file = trim(file_get_contents("/boot/config/sriov.cfg"));
|
||||
$file = preg_replace('/^VFS=/', '', $file); // Remove prefix
|
||||
$entries = preg_split('/\s+/', $file, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
if (preg_match($DBDF_SRIOV_REGEX, $entry)) {
|
||||
// Format: <DBDF>|<Vendor:Device>|<VF_count>
|
||||
[$dbdf, $ven_dev, $vf_count] = explode('|', $entry);
|
||||
$sriov_devices[$dbdf] = [
|
||||
'dbdf' => $dbdf,
|
||||
'vendor' => $ven_dev,
|
||||
'vf_count' => (int)$vf_count,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
# $sriov_devices = array_values(array_unique($sriov_devices, SORT_REGULAR));
|
||||
foreach ($entries as $entry) {
|
||||
if (preg_match($DBDF_SRIOV_REGEX, $entry)) {
|
||||
// Format: <DBDF>|<Vendor:Device>|<VF_count>
|
||||
[$dbdf, $ven_dev, $vf_count] = explode('|', $entry);
|
||||
$sriov_devices[$dbdf] = [
|
||||
'dbdf' => $dbdf,
|
||||
'vendor' => $ven_dev,
|
||||
'vf_count' => (int)$vf_count,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sriov_devices;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
@@ -311,27 +302,25 @@ function parseVFSettings() {
|
||||
$sriov_devices_settings = [];
|
||||
$DBDF_SRIOV_SETTINGS_REGEX = '/^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}\.[[:xdigit:]]\|[[:xdigit:]]{4}:[[:xdigit:]]{4}\|[01]\|([[:xdigit:]]{2}:){5}[[:xdigit:]]{2}$/';
|
||||
if (is_file("/boot/config/sriovvfs.cfg")) {
|
||||
$file = trim(file_get_contents("/boot/config/sriovvfs.cfg"));
|
||||
$file = preg_replace('/^VFSETTINGS=/', '', $file); // Remove prefix
|
||||
$entries = preg_split('/\s+/', $file, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$file = trim(file_get_contents("/boot/config/sriovvfs.cfg"));
|
||||
$file = preg_replace('/^VFSETTINGS=/', '', $file); // Remove prefix
|
||||
$entries = preg_split('/\s+/', $file, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
if (preg_match($DBDF_SRIOV_SETTINGS_REGEX, $entry)) {
|
||||
// Format: <DBDF>|<Vendor:Device>|<VFIO_flag>|<MAC>
|
||||
[$dbdf, $ven_dev, $vfio_flag, $mac] = explode('|', $entry);
|
||||
if ($mac == "00:00:00:00:00:00") $mac = "";
|
||||
$sriov_devices_settings[$dbdf] = [
|
||||
'dbdf' => $dbdf,
|
||||
'vendor' => $ven_dev,
|
||||
'vfio' => (int)$vfio_flag,
|
||||
'mac' => strtoupper($mac),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
# $sriov_devices_settings = array_values(array_unique($sriov_devices_settings, SORT_REGULAR));
|
||||
foreach ($entries as $entry) {
|
||||
if (preg_match($DBDF_SRIOV_SETTINGS_REGEX, $entry)) {
|
||||
// Format: <DBDF>|<Vendor:Device>|<VFIO_flag>|<MAC>
|
||||
[$dbdf, $ven_dev, $vfio_flag, $mac] = explode('|', $entry);
|
||||
if ($mac == "00:00:00:00:00:00") $mac = "";
|
||||
$sriov_devices_settings[$dbdf] = [
|
||||
'dbdf' => $dbdf,
|
||||
'vendor' => $ven_dev,
|
||||
'vfio' => (int)$vfio_flag,
|
||||
'mac' => strtoupper($mac),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sriov_devices_settings;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,14 +353,14 @@ function setVfMacAddress(string $vf_pci, string $mac, ?string $rebindDriver = nu
|
||||
];
|
||||
|
||||
if (!is_dir($vf_path)) {
|
||||
$result['error'] = "VF path not found: $vf_path";
|
||||
$result['error'] = _("VF path not found").": $vf_path";
|
||||
return $result;
|
||||
}
|
||||
|
||||
// --- Find parent PF (Physical Function) ---
|
||||
$pf_link = "$vf_path/physfn";
|
||||
if (!is_link($pf_link)) {
|
||||
$result['error'] = "No PF link for $vf_pci (not an SR-IOV VF?)";
|
||||
$result['error'] = sprintf("No PF link for %s (not an SR-IOV VF?)",$vf_pci);
|
||||
return $result;
|
||||
}
|
||||
$pf_pci = basename(readlink($pf_link));
|
||||
@@ -381,7 +370,7 @@ function setVfMacAddress(string $vf_pci, string $mac, ?string $rebindDriver = nu
|
||||
$pf_net = glob("/sys/bus/pci/devices/{$pf_pci}/net/*");
|
||||
$pf_iface = ($pf_net && isset($pf_net[0])) ? basename($pf_net[0]) : null;
|
||||
if (!$pf_iface) {
|
||||
$result['error'] = "Could not detect PF interface for $pf_pci";
|
||||
$result['error'] = _("Could not detect PF interface for")." $pf_pci";
|
||||
return $result;
|
||||
}
|
||||
$result['pf_iface'] = $pf_iface;
|
||||
@@ -389,7 +378,7 @@ function setVfMacAddress(string $vf_pci, string $mac, ?string $rebindDriver = nu
|
||||
// --- Detect VF index ---
|
||||
$vf_index = getVfIndex($pf_pci, $vf_pci);
|
||||
if ($vf_index === null) {
|
||||
$result['error'] = "Could not determine VF index for $vf_pci under $pf_pci";
|
||||
$result['error'] = sprintf(_("Could not determine VF index for %s under %s"),$vf_pci,$pf_pci);
|
||||
return $result;
|
||||
}
|
||||
$result['vf_index'] = $vf_index;
|
||||
@@ -406,7 +395,7 @@ function setVfMacAddress(string $vf_pci, string $mac, ?string $rebindDriver = nu
|
||||
file_put_contents($unbind_path, $vf_pci);
|
||||
$result['unbind'] = true;
|
||||
} else {
|
||||
$result['error'] = "Cannot unbind VF $vf_pci from $vf_driver (permissions)";
|
||||
$result['error'] = sprintf(_("Cannot unbind VF %s from %s (permissions)"),$vf_pci,$vf_driver);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -424,7 +413,7 @@ function setVfMacAddress(string $vf_pci, string $mac, ?string $rebindDriver = nu
|
||||
if ($ret === 0) {
|
||||
$result['mac_set'] = true;
|
||||
} else {
|
||||
$result['error'] = "Failed to set MAC: " . implode("; ", $output);
|
||||
$result['error'] = _("Failed to set MAC").": " . implode("; ", $output);
|
||||
}
|
||||
|
||||
// --- Rebind logic ---
|
||||
@@ -437,7 +426,7 @@ function setVfMacAddress(string $vf_pci, string $mac, ?string $rebindDriver = nu
|
||||
$result['rebind'] = true;
|
||||
$result['driver_after'] = $target_driver;
|
||||
} else {
|
||||
$result['error'] = "Cannot rebind VF $vf_pci to $target_driver (permissions)";
|
||||
$result['error'] = sprintf(_("Cannot rebind VF %s to %s (permissions)"),$vf_pci,$target_driver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/php
|
||||
#!/usr/bin/php
|
||||
<?PHP
|
||||
function SysDriverslog($m, $type='NOTICE') {
|
||||
if ($type == 'DEBUG') return;
|
||||
|
||||
@@ -20,6 +20,7 @@ require_once "$docroot/webGui/include/Secure.php";
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
require_once "$docroot/webGui/include/SriovHelpers.php";
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
function json_response($success, $error = null, $details = [])
|
||||
{
|
||||
@@ -41,10 +42,29 @@ function json_response($success, $error = null, $details = [])
|
||||
'details' => $details
|
||||
];
|
||||
|
||||
echo json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
$json = json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
file_put_contents("/tmp/vfactionjson",$json);
|
||||
echo $json;
|
||||
exit;
|
||||
}
|
||||
|
||||
function safe_file_put_contents($path, $data)
|
||||
{
|
||||
// Check that the file or its directory is writable
|
||||
$dir = is_dir($path) ? $path : dirname($path);
|
||||
if (!is_writable($dir)) {
|
||||
throw new RuntimeException("Path not writable: $path");
|
||||
}
|
||||
|
||||
$result = @file_put_contents($path, $data);
|
||||
if ($result === false) {
|
||||
$err = error_get_last();
|
||||
throw new RuntimeException("Failed to write to $path: " . ($err['message'] ?? 'unknown error'));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
function action_settings($pciid)
|
||||
{
|
||||
@@ -64,6 +84,8 @@ function action_settings($pciid)
|
||||
// Skip if no action needed
|
||||
if ($vfio == 0 && $mac == "") continue;
|
||||
|
||||
if ($mac == "") $mac="00:00:00:00:00:00";
|
||||
|
||||
$cmd = "/usr/local/sbin/sriov-vfsettings.sh " .
|
||||
escapeshellarg($vfpci) . " " .
|
||||
escapeshellarg($vf['vd']) . " " .
|
||||
@@ -77,19 +99,17 @@ function action_settings($pciid)
|
||||
// Clean output: remove blank lines and trim whitespace
|
||||
$output = array_filter(array_map('trim', $output));
|
||||
|
||||
file_put_contents("/tmp/vfaction", "$ret\n$cmd\n" . json_encode($output) . "\n");
|
||||
|
||||
if ($ret !== 0) {
|
||||
// Only include relevant lines for error reporting
|
||||
$results[] = [
|
||||
$results[$vfpci] = [
|
||||
'success' => false,
|
||||
'error' => implode("\n", $output) ?: "Unknown error (exit code $ret)"
|
||||
'error' => implode("\n", $output) ?: sprintf(_("Unknown error (exit code %s)"),$ret)
|
||||
];
|
||||
} else {
|
||||
// Success: include minimal details or last few lines
|
||||
$results[] = [
|
||||
$results[$vfpci] = [
|
||||
'success' => true,
|
||||
'details' => 'Applied VF settings'
|
||||
'details' => _('Applied VF settings')
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -103,47 +123,63 @@ $pciid = _var($_POST, 'pciid');
|
||||
$vd = _var($_POST, 'vd');
|
||||
|
||||
if (!isset($pciid) || !isset($vd)) {
|
||||
echo json_response(false, "Missing PCI ID or virtual device");
|
||||
echo json_response(false, _("Missing PCI ID or virtual device"));
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch ($type) {
|
||||
// --------------------------------------------------------
|
||||
// SR-IOV enable/disable & VF count changes
|
||||
// --------------------------------------------------------
|
||||
case "sriov":
|
||||
$numvfs = _var($_POST, 'numvfs');
|
||||
$numvfs = _var($_POST, 'numvfs');
|
||||
$currentvfs = _var($_POST, 'currentvfs');
|
||||
$filepath = "/sys/bus/pci/devices/$pciid/sriov_numvfs";
|
||||
$filepath = "/sys/bus/pci/devices/$pciid/sriov_numvfs";
|
||||
|
||||
if (!is_writable($filepath)) {
|
||||
echo json_response(false, "Cannot modify $filepath");
|
||||
break;
|
||||
json_response(false, "Cannot modify $filepath");
|
||||
}
|
||||
|
||||
try {
|
||||
// Disable all VFs
|
||||
if ($numvfs == 0) {
|
||||
file_put_contents($filepath, 0);
|
||||
echo json_response(true, null, ["Disabled all VFs"]);
|
||||
break;
|
||||
safe_file_put_contents($filepath, 0);
|
||||
json_response(true, null, [_("Disabled all VFs")]);
|
||||
}
|
||||
|
||||
// Change VF count
|
||||
if ($numvfs != $currentvfs) {
|
||||
file_put_contents($filepath, 0);
|
||||
file_put_contents($filepath, $numvfs);
|
||||
safe_file_put_contents($filepath, 0);
|
||||
safe_file_put_contents($filepath, $numvfs);
|
||||
|
||||
$results = action_settings($pciid);
|
||||
echo json_response(true, null, ["Changed VF count to $numvfs", $results]);
|
||||
break;
|
||||
|
||||
$all_success = array_reduce($results, fn($ok, $r) => $ok && ($r['success'] ?? false), true);
|
||||
|
||||
safe_file_put_contents(
|
||||
"/tmp/vfactionres2",
|
||||
json_encode(['all_success' => $all_success, 'results' => $results], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
|
||||
);
|
||||
|
||||
json_response(true, null, [sprintf(_("Changed VF count to %d"), $numvfs), $results]);
|
||||
}
|
||||
|
||||
file_put_contents($filepath, $numvfs);
|
||||
// Reapply VF settings
|
||||
safe_file_put_contents($filepath, $numvfs);
|
||||
$results = action_settings($pciid);
|
||||
echo json_response(true, null, ["Reapplied VF settings", $results]);
|
||||
json_response(true, null, [_("Reapplied VF settings"), $results]);
|
||||
|
||||
} catch (Throwable $e) {
|
||||
echo json_response(false, "Failed to change VF count: " . $e->getMessage());
|
||||
// Log internal details for debugging
|
||||
@file_put_contents("/tmp/vfaction_error.log", date('c') . " " . $e->getMessage() . "\n", FILE_APPEND);
|
||||
|
||||
json_response(false, _("Failed to change VF count") . ": " . $e->getMessage());
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// --------------------------------------------------------
|
||||
// VF driver binding, MAC changes
|
||||
// --------------------------------------------------------
|
||||
@@ -181,9 +217,9 @@ switch ($type) {
|
||||
}
|
||||
|
||||
// Nothing changed
|
||||
echo json_response(true, null, ["No changes detected"]);
|
||||
echo json_response(true, null, [_("No changes detected")]);
|
||||
} catch (Throwable $e) {
|
||||
echo json_response(false, "Error applying VF settings: " . $e->getMessage());
|
||||
echo json_response(false, _("Error applying VF settings").": ".$e->getMessage());
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -197,7 +233,7 @@ switch ($type) {
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_response(false, "Unknown request type: $type");
|
||||
echo json_response(false, _("Unknown request type").": $type");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ if (isset($pciid) && isset($vd)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$found) $newexplode[] = $newelement_check.$numvfs;
|
||||
if (!$found && $numvfs != "0") $newexplode[] = $newelement_check.$numvfs;
|
||||
$new = "VFS=".implode(" ",$newexplode);
|
||||
$file = $sriov;
|
||||
break;
|
||||
@@ -73,10 +73,10 @@ if (isset($pciid) && isset($vd)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$found) $newexplode[] = $newelement_check.$vfio."|".$mac;
|
||||
$new = "VFSETTINGS=".implode(" ",$newexplode);
|
||||
$file = $sriovvfs;
|
||||
break;
|
||||
if (!$found && $vfio != 0) $newexplode[] = $newelement_check.$vfio."|".$mac;
|
||||
$new = "VFSETTINGS=".implode(" ",$newexplode);
|
||||
$file = $sriovvfs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user