mirror of
https://github.com/unraid/webgui.git
synced 2025-12-31 22:50:25 -06:00
- Wrapped tables in CPUisol.page, CPUpin.page, and CPUvms.page with a new div class for improved layout structure. - Added a new CSS class .TableContainer--no-min-width to handle overflow and ensure consistent table presentation. - This change continues the effort to enhance visual consistency across the plugin.
341 lines
11 KiB
Plaintext
341 lines
11 KiB
Plaintext
Menu="CPUset:1"
|
|
Title="CPU Pinning VM"
|
|
Tag="icon-cpu"
|
|
---
|
|
<?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.
|
|
*/
|
|
?>
|
|
<?
|
|
$libvirtd = pgrep('libvirtd')!==false;
|
|
$dockerd = pgrep('dockerd')!==false;
|
|
|
|
$cpus = cpu_list();
|
|
$total = count($cpus);
|
|
$spinner = "<tr><td colspan='".($total+2)."'><div class='spinner'></div></td></tr>";
|
|
$cpuset = implode(';',$cpus);
|
|
$is_intel_cpu = is_intel_cpu();
|
|
$core_types = $is_intel_cpu ? get_intel_core_types() : [];
|
|
|
|
function create($type = "") {
|
|
// create the table header. Make multiple rows when CPU cores are many ;)
|
|
global $total, $cpus, $is_intel_cpu, $core_types;
|
|
$loop = floor(($total-1)/32) + 1;
|
|
$text = [];
|
|
for ($c = 0; $c < $loop; $c++) {
|
|
$max = ($c == $loop-1 ? ($total % 32 ?: 32) : 32);
|
|
for ($n = 0; $n < $max; $n++) {
|
|
[$cpu1, $cpu2] = my_preg_split('/[,-]/', $cpus[$c * 32 + $n]);
|
|
if (empty($text[$n])) $text[$n] = '';
|
|
$text[$n] .= "$cpu1<br>";
|
|
if ($cpu2) $text[$n] .= "$cpu2<br>";
|
|
}
|
|
}
|
|
$label = implode('<br>', array_fill(0, $loop, 'CORES:' . ($cpu2 ? '<br>CORES:' : '')));
|
|
if ($type == "vm") echo "<th>VCPUS</th>";
|
|
|
|
echo "<th>$label</th>" . implode(array_map(function($t) {
|
|
global $is_intel_cpu, $core_types;
|
|
[$cpu1, $cpu2] = my_preg_split('/[,<br>]/',$t);
|
|
if ($is_intel_cpu && count($core_types) > 0) $core_type = "$core_types[$cpu1]"; else $core_type = "";
|
|
return "<th title='$core_type'>$t</th>";
|
|
}, $text));
|
|
}
|
|
?>
|
|
|
|
<script>
|
|
String.prototype.strip = function(){return this.replace(/ |\(|\)|\[|\]/g,'');}
|
|
String.prototype.encode = function(){return this.replace(/\./g,'%2e');}
|
|
|
|
function apply(form) {
|
|
/* disable buttons */
|
|
$(form).find('input[value="_(Apply)_"]').prop('disabled', true);
|
|
$(form).find('input[value="_(Reset)_"]').val("_(Done)_").prop('onclick', null).off('click').click(function() {
|
|
done();
|
|
});
|
|
$('input[value="_(Done)_"]').prop('disabled', true);
|
|
var id = $(form).prop('name');
|
|
var args = {
|
|
'id': id,
|
|
'names': form.names.value.encode(),
|
|
'cpus': {},
|
|
'cores': {}
|
|
};
|
|
|
|
/* get the 'checked' cpus */
|
|
$(form).find('input[type=checkbox]').each(function() {
|
|
if ($(this).prop('checked')) {
|
|
args['cpus'][$(this).prop('name').encode()] = 'on';
|
|
}
|
|
});
|
|
|
|
/* get the vcpus */
|
|
$(form).find('select').each(function() {
|
|
if ($(this).prop('id')) {
|
|
args['cores'][$(this).prop('name').encode()] = $(this).prop('value');
|
|
}
|
|
});
|
|
|
|
/* show the instant wait message */
|
|
$('#wait-' + id).show();
|
|
|
|
/* step 1: prepare the update and report back the changes */
|
|
$.post('/webGui/include/UpdateOne.php', {
|
|
data: JSON.stringify(args)
|
|
}, function(reply) {
|
|
if (reply.error) {
|
|
swal({
|
|
type: 'error',
|
|
title: "_(Assignment error)_",
|
|
text: reply.error,
|
|
html: true,
|
|
confirmButtonText: "_(Ok)_"
|
|
}, function() {
|
|
$('#wait-' + id).hide();
|
|
$(form).find('input[value="_(Done)_"]').val("_(Reset)_").prop('disabled', false).prop('onclick', null).off('click').click(function() {
|
|
reset($('form[name="' + id + '"]'));
|
|
});
|
|
});
|
|
} else if (reply.success) {
|
|
var data = reply.success.split(';');
|
|
wait = data.length;
|
|
for (var i = 0; i < data.length; i++) {
|
|
var name = data[i];
|
|
//$('#' + id + '-' + name.strip()).show('slow');
|
|
$('#' + CSS.escape(id + '-' + name.strip().replace(/!/g, '%21'))).show('slow');
|
|
|
|
/* step 2: apply the changes by updating the vm or container */
|
|
$.post('/webGui/include/UpdateTwo.php', {
|
|
id: id,
|
|
name: encodeURIComponent(name)
|
|
}, function(reply) {
|
|
if (reply.error) {
|
|
/* report error and reload table */
|
|
swal({
|
|
type: 'error',
|
|
title: "_(Execution error)_",
|
|
text: reply.error,
|
|
html: true,
|
|
confirmButtonText: "_(Ok)_"
|
|
}, function() {
|
|
$('#wait-' + id).hide();
|
|
$('input[value="_(Done)_"]').prop('disabled', false);
|
|
reset($('form[name="' + id + '"]'));
|
|
});
|
|
} else {
|
|
$('#' + id + '-' + reply.success.strip()).hide('slow');
|
|
/* cleanup when all is done */
|
|
if (!--wait) {
|
|
setTimeout(function() {
|
|
$('#wait-' + id).hide();
|
|
}, 500);
|
|
$('input[value="_(Done)_"]').prop('disabled', false);
|
|
/* isolated cpus, need reboot notice? */
|
|
if (id == 'is') notice();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
$('#wait-' + id).hide();
|
|
$('input[value="_(Done)_"]').prop('disabled', false);
|
|
if (id == 'is') notice();
|
|
}
|
|
});
|
|
}
|
|
|
|
/* Function to fetch current VM assignments and update the table */
|
|
function vm() {
|
|
$.post('/webGui/include/CPUset.php', { id: 'vm', cpus: '<?=$cpuset?>' }, function(d) {
|
|
var data = d.split('\0');
|
|
$('#table-vm').html(data[0]);
|
|
$('#names-vm').val(data[1]);
|
|
buttons(document.vm);
|
|
});
|
|
}
|
|
|
|
/* Function to toggle thread assignment for containers */
|
|
function thread2containers(n) {
|
|
const selector = $('form[name=ct]').find(`[name$=":${n}"]`);
|
|
const checkboxes = selector.length;
|
|
const checked = selector.filter(':checked').length;
|
|
selector.prop('checked', (checkboxes - checked > checked ? true : false)).change();
|
|
}
|
|
|
|
/* Function to fetch current container assignments and update the table */
|
|
function ct() {
|
|
$.post('/webGui/include/CPUset.php', { id: 'ct', cpus: '<?=$cpuset?>' }, function(d) {
|
|
var data = d.split('\0');
|
|
$('#table-ct').html(data[0]);
|
|
$('#names-ct').val(data[1]);
|
|
buttons(document.ct);
|
|
|
|
/* Inject thread to containers toggles */
|
|
if ($('a[onclick^="thread2containers"]').length === 0) {
|
|
$('form[name=ct]').find('thead tr th:gt(1)').each((i, elem) => {
|
|
// Preserve the existing title if available
|
|
let existingTitle = elem.title || "";
|
|
let newTitle = existingTitle ? existingTitle + " _(Toggle thread to containers)_" : "_(Toggle thread to containers)_";
|
|
elem.innerHTML = elem.innerHTML.replace(/(\d+)/g, '<a href="#" onclick="thread2containers(this.innerText);return false;" title="' + newTitle + '">$1</a>');
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/* Function to fetch current isolated CPU assignments and update the table */
|
|
function is() {
|
|
$.post('/webGui/include/CPUset.php', { id: 'is', cpus: '<?=$cpuset?>' }, function(d) {
|
|
$('#table-is').html(d);
|
|
buttons(document.is);
|
|
<?if ($safemode):?>
|
|
$('#table-is').find('input[type=checkbox]').prop('disabled', true);
|
|
<?endif;?>
|
|
});
|
|
}
|
|
|
|
/* Function to display a notice to reboot the system after changes */
|
|
function notice() {
|
|
var message = "_(CPU Isolation: A reboot is required to apply changes)_";
|
|
|
|
$.post('/webGui/include/CPUset.php', { id: 'cmd' }, function(d) {
|
|
if (d == 1) addRebootNotice(message); else removeRebootNotice(message);
|
|
});
|
|
}
|
|
|
|
/* Function to reset form changes without a complete page refresh */
|
|
function reset(form) {
|
|
$(form).find('input[value="_(Apply)_"]').prop('disabled', true);
|
|
$(form).find('input[value="_(Reset)_"]').val("_(Done)_").prop('onclick', null).off('click').click(function() { done(); });
|
|
|
|
switch ($(form).prop('name')) {
|
|
case 'vm':
|
|
$('#table-vm').html("<?=$spinner?>");
|
|
$('div.spinner').html(unraid_logo);
|
|
vm();
|
|
break;
|
|
case 'ct':
|
|
$('#table-ct').html("<?=$spinner?>");
|
|
$('div.spinner').html(unraid_logo);
|
|
ct();
|
|
break;
|
|
case 'is':
|
|
$('#table-is').html("<?=$spinner?>");
|
|
$('div.spinner').html(unraid_logo);
|
|
is();
|
|
break;
|
|
}
|
|
}
|
|
|
|
function vcpupins($button) {
|
|
|
|
var $name = $button.id.split(";")[1];
|
|
var $value = $button.value;
|
|
var $form = $('form[name="vm"]');
|
|
|
|
if ($value == "_(Select All)_") {
|
|
$("[class='vcpu-" + $name + "']").prop('checked', true);
|
|
var $cores = $("[class='vcpu-" + $name + "']:checked");
|
|
$("[class~='vcpus-" + $name + "']").prop("disabled", true).prop("value", $cores.length);
|
|
$button.value = "_(Deselect All)_";
|
|
$($form).find('input[value="_(Apply)_"]').prop('disabled', false);
|
|
} else {
|
|
$("[class='vcpu-" + $name + "']").prop('checked', false);
|
|
$("[class~='vcpus-" + $name + "']").prop("disabled", false).prop("value", 1);
|
|
$button.value = "_(Select All)_";
|
|
$($form).find('input[value="_(Apply)_"]').prop('disabled', false);
|
|
}
|
|
}
|
|
|
|
/* Function to handle checkbox interactions and enable/disable form buttons */
|
|
function buttons(form) {
|
|
$(form).find('input[type=checkbox]').each(function() {
|
|
$(this).on('change', function() {
|
|
var total = $(form).find('input[type=checkbox]').length;
|
|
var checked = 'input[name^="' + $(this).prop('name').split(':')[0] + ':' + '"]:checked';
|
|
var cores = $(form).find(checked).length;
|
|
|
|
/* Ensure VMs have at least one core selected */
|
|
if ($(form).prop('name') == 'vm' ) {
|
|
value = $(this).prop('name').split(':')[0];
|
|
var testname = "vmbtnvCPUSelect" + value ;
|
|
if (cores < 1)
|
|
{
|
|
$(form).find('select[name="' + $(this).prop('name').split(':')[0] + '"]').prop('disabled', false).prop("value",1);
|
|
$(form).find('input[name="vmbtnvCPUSelect' + $(this).prop('name').split(':')[0] + '"]').prop("value","_(Select All)_");
|
|
} else {
|
|
$(form).find('select[name="' + $(this).prop('name').split(':')[0] + '"]').prop('disabled', true).prop("value",cores);
|
|
$(form).find('input[name="vmbtnvCPUSelect' + $(this).prop('name').split(':')[0] + '"]').prop("value","_(Deselect All)_");
|
|
}
|
|
}
|
|
/* Ensure isolation does not have all cores selected */
|
|
if ($(form).prop('name') == 'is' && $(this).prop('checked')) $(this).prop('checked', cores < total);
|
|
|
|
/* Enable Apply and Done buttons when changes are made */
|
|
$(form).find('input[value="_(Apply)_"]').prop('disabled', false);
|
|
$(form).find('input[value="_(Done)_"]').val("_(Reset)_").prop('onclick', null).off('click').click(function() { reset(form); });
|
|
});
|
|
});
|
|
$(form).find('select').each(function() {
|
|
$(this).on('change', function() {
|
|
var total = $(form).find('input[type=checkbox]').length;
|
|
var checked = 'input[name^="' + $(this).prop('name').split(':')[0] + ':' + '"]:checked';
|
|
var cores = $(form).find(checked).length;
|
|
|
|
/* Enable Apply and Done buttons when changes are made */
|
|
$(form).find('input[value="_(Apply)_"]').prop('disabled', false);
|
|
$(form).find('input[value="_(Done)_"]').val("_(Reset)_").prop('onclick', null).off('click').click(function() { reset(form); });
|
|
});
|
|
});
|
|
}
|
|
|
|
/* Initialize the functions on document ready */
|
|
$(function() {
|
|
<?if ($libvirtd):?>
|
|
vm();
|
|
<?endif;?>
|
|
<?if ($dockerd):?>
|
|
ct();
|
|
<?endif;?>
|
|
is();
|
|
notice();
|
|
});
|
|
</script>
|
|
<?if ($libvirtd):?>
|
|
<form name="vm">
|
|
<input type="hidden" name="names" id="names-vm" value="">
|
|
|
|
<div class="TableContainer--no-min-width">
|
|
<table class="tablesorter shift" style="width: auto">
|
|
<thead>
|
|
<tr>
|
|
<th><i class="fa fa-list"></i> _(VM)_</th>
|
|
<?create("vm")?>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="table-vm"><?=$spinner?></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="flex flex-row items-center gap-2">
|
|
<input type="button" value="_(Apply)_" onclick="apply(this.form)" disabled>
|
|
<input type="button" value="_(Done)_" onclick="done()">
|
|
<span id="wait-vm" class="red-text" style="display:none">
|
|
<i class="fa fa-spinner fa-spin"></i>
|
|
<span>_(Please wait)_...</span>
|
|
</span>
|
|
</div>
|
|
</form>
|
|
<?else:?>
|
|
<div class="notice">_(No CPU pinning available. VM service must be started)_</div>
|
|
<?endif;?>
|
|
|
|
:cpu_vms_help:
|