mirror of
https://github.com/unraid/webgui.git
synced 2026-03-10 21:10:01 -05:00
Merge pull request #1757 from dlandon/master
Zfs pool information and fix btrfs stats and usage displays and CPU pinning post too many variables php error.
This commit is contained in:
@@ -24,165 +24,228 @@ $spinner = "<tr><td colspan='".($total+2)."'><div class='spinner'></div></td></t
|
||||
$cpuset = implode(';',$cpus);
|
||||
|
||||
function create() {
|
||||
// create the table header. Make multiple rows when CPU cores are many ;)
|
||||
global $total,$cpus;
|
||||
$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,'CPU:'.($cpu2 ? '<br>HT:':'')));
|
||||
echo "<th>$label</th>".implode(array_map(function($t){return "<th>$t</th>";},$text));
|
||||
// create the table header. Make multiple rows when CPU cores are many ;)
|
||||
global $total, $cpus;
|
||||
$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, 'CPU:' . ($cpu2 ? '<br />CPU:' : '')));
|
||||
echo "<th>$label</th>" . implode(array_map(function($t) {
|
||||
return "<th>$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 wait = 0;
|
||||
var id = $(form).prop('name');
|
||||
var args = {};
|
||||
args['id'] = id;
|
||||
args['names'] = form.names.value.encode();
|
||||
// get the 'checked' cpus
|
||||
$(form).find('input[type=checkbox]').each(function(){
|
||||
if ($(this).prop('checked')) args[$(this).prop('name').encode()] = 'on';
|
||||
});
|
||||
// show the instant wait message
|
||||
$('#wait-'+id).show();
|
||||
// step 1: prepare the update and report back the changes
|
||||
$.post('/webGui/include/UpdateOne.php',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');
|
||||
// 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 vm() {
|
||||
// fetch the current vm assignments
|
||||
$.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 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 ct() {
|
||||
// fetch the current container assignments
|
||||
$.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) => {
|
||||
elem.innerHTML = elem.innerHTML.replace(/(\d+)/g, '<a href="#" onclick="thread2containers(this.innerText);return false;" title="_(Toggle thread to containers)_">$1</a>');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function is() {
|
||||
// fetch the current isolcpu assignments
|
||||
$.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 notice() {
|
||||
// notice to reboot system after changes
|
||||
var message = "_(CPU Isolation: A reboot is required to apply changes)_";
|
||||
/* 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': {}
|
||||
};
|
||||
|
||||
$.post('/webGui/include/CPUset.php',{id:'cmd'},function(d){
|
||||
if (d==1) addRebootNotice(message); else removeRebootNotice(message);
|
||||
});
|
||||
/* get the 'checked' cpus */
|
||||
$(form).find('input[type=checkbox]').each(function() {
|
||||
if ($(this).prop('checked')) {
|
||||
args['cpus'][$(this).prop('name').encode()] = 'on';
|
||||
}
|
||||
});
|
||||
|
||||
/* 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');
|
||||
|
||||
/* 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) => {
|
||||
elem.innerHTML = elem.innerHTML.replace(/(\d+)/g, '<a href="#" onclick="thread2containers(this.innerText);return false;" title="_(Toggle thread to containers)_">$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) {
|
||||
// undo changes without a complete refresh of the page
|
||||
$(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;
|
||||
}
|
||||
$(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 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;
|
||||
// vms must have at least one core selected
|
||||
if ($(form).prop('name')=='vm') $(form).find(checked).prop('disabled',cores<2);
|
||||
// isolation may not have all cores selected
|
||||
if ($(form).prop('name')=='is' && $(this).prop('checked')) $(this).prop('checked',cores<total);
|
||||
// we need the Apply and Done buttons react on checkbox changes
|
||||
$(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('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') $(form).find(checked).prop('disabled', cores < 2);
|
||||
|
||||
/* 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); });
|
||||
});
|
||||
});
|
||||
}
|
||||
$(function(){
|
||||
|
||||
/* Initialize the functions on document ready */
|
||||
$(function() {
|
||||
<?if ($libvirtd):?>
|
||||
vm();
|
||||
vm();
|
||||
<?endif;?>
|
||||
<?if ($dockerd):?>
|
||||
ct();
|
||||
ct();
|
||||
<?endif;?>
|
||||
is();
|
||||
notice();
|
||||
is();
|
||||
notice();
|
||||
});
|
||||
</script>
|
||||
<?if ($libvirtd):?>
|
||||
|
||||
@@ -770,7 +770,7 @@ _(Critical disk utilization threshold)_ (%):
|
||||
<form markdown="1" method="POST" action="/update.php" target="progressFrame">
|
||||
|
||||
_(pool device stats)_:
|
||||
: <?echo "<pre>".shell_exec("/sbin/btrfs dev stats -T /mnt/$tag")."</pre>"?>
|
||||
: <?echo "<pre>" . htmlspecialchars(shell_exec("/sbin/btrfs dev stats -T ".escapeshellarg("/mnt/$tag")), ENT_QUOTES, 'UTF-8') . "</pre>"; ?>
|
||||
|
||||
<input type="hidden" name="#command" value="/webGui/scripts/btrfs_check">
|
||||
<input type="hidden" name="#arg[1]" value="reset">
|
||||
@@ -784,11 +784,11 @@ _(pool device stats)_:
|
||||
<div class="title nocontrol"><span class="left"><i class="title fa fa-balance-scale"></i>_(Balance Status)_</span></div>
|
||||
<form markdown="1" method="POST" action="/update.php" target="progressFrame" onsubmit="prepareFS(this,'btrfs-balance-<?=$tag?>','/mnt/<?=$tag?>')">
|
||||
<?if (_var($disk,'fsStatus')=="Mounted"):?>
|
||||
<?exec("$docroot/webGui/scripts/btrfs_balance status /mnt/$tag", $balance_status, $retval)?>
|
||||
<?$usage = exec("/sbin/btrfs fi usage /mnt/$tag|grep -Pom1 '^Data,.+ \\(\\K[^%]+'");?>
|
||||
<?exec("$docroot/webGui/scripts/btrfs_balance status ".escapeshellarg("/mnt/$tag"), $balance_status, $retval)?>
|
||||
<?$usage = exec("/sbin/btrfs fi usage ".escapeshellarg("/mnt/$tag")." | grep -Pom1 '^Data,.+ \\(\\K[^%]+'");?>
|
||||
|
||||
_(btrfs filesystem usage)_:
|
||||
: <?echo "<pre>".shell_exec("/sbin/btrfs fi usage -T /mnt/$tag")."</pre>"?>
|
||||
: <?echo "<pre>" . htmlspecialchars(shell_exec("/sbin/btrfs fi usage -T ".escapeshellarg("/mnt/$tag")), ENT_QUOTES, 'UTF-8') . "</pre>"; ?>
|
||||
|
||||
_(btrfs balance status)_:
|
||||
: <?echo "<pre id='btrfs-balance'>".implode("\n", $balance_status)."</pre>"?>
|
||||
@@ -901,7 +901,7 @@ _(Block group usage)_ (%):
|
||||
<div class="title nocontrol"><span class="left"><i class="title fa fa-paint-brush"></i>_(Scrub Status)_</span></div>
|
||||
<form markdown="1" method="POST" action="/update.php" target="progressFrame" onsubmit="prepareFS(this,'btrfs-scrub-<?=$tag?>','/mnt/<?=$tag?>')">
|
||||
<?if (_var($disk,'fsStatus')=="Mounted"):?>
|
||||
<?exec("$docroot/webGui/scripts/btrfs_scrub status /mnt/$tag", $scrub_status, $retval)?>
|
||||
<?exec("$docroot/webGui/scripts/btrfs_scrub status ".escapeshellarg("/mnt/$tag"), $scrub_status, $retval)?>
|
||||
|
||||
_(btrfs scrub status)_:
|
||||
: <?echo "<pre id='btrfs-scrub'>".implode("\n", $scrub_status)."</pre>"?>
|
||||
@@ -943,6 +943,7 @@ _(btrfs scrub status)_:
|
||||
<input type="hidden" name="#include" value="/webGui/include/update.btrfs.php">
|
||||
<input type="hidden" name="#job" value="scrub_<?=$tag?>;<?=$docroot?>/plugins/dynamix/scripts/btrfs_scrub start /mnt/<?=$tag?> -r">
|
||||
<input type="hidden" name="hour" value="">
|
||||
|
||||
_(Scrub schedule)_:
|
||||
: <select name="mode" onchange="presetBTRFS(this.form,'#scrub-hour')">
|
||||
<?for ($m=0; $m<count($mode); $m++):?>
|
||||
@@ -1030,10 +1031,10 @@ _(btrfs check status)_:
|
||||
</form>
|
||||
<?endif;?>
|
||||
<?if (fsType('zfs') && !isSubpool($name)):?>
|
||||
<div class="title nocontrol"><span class="left"><i class="title fa fa-info"></i>_(Pool Status)_</span></div>
|
||||
<div class="title nocontrol"><span class="left"><i class="title fa fa-hdd-o"></i>_(Pool Status)_</span></div>
|
||||
<form markdown="1" method="POST" action="/update.php" target="progressFrame" onsubmit="prepareFS(this,'zfs-scrub-<?=$tag?>','<?=$tag?>')">
|
||||
<?if (_var($disk,'fsStatus')=="Mounted"):?>
|
||||
<?exec("$docroot/webGui/scripts/zfs_scrub status $tag", $zfs_status, $retval); $zfs_status = implode("\n",$zfs_status)?>
|
||||
<?exec("$docroot/webGui/scripts/zfs_scrub status ".escapeshellarg($tag), $zfs_status, $retval); $zfs_status = implode("\n",$zfs_status)?>
|
||||
<?$zfs_cmd = strpos($zfs_status,"'zpool clear'")===false ? 'start' : 'clear'?>
|
||||
|
||||
_(zfs pool status)_:
|
||||
@@ -1066,15 +1067,23 @@ _(zfs pool status)_:
|
||||
: <input type="submit" value="_(Scrub)_" disabled><?=!$tag||$tag==prefix($tag) ? "<b>"._('Scrub')."</b> "._('is only available when the filesyestem is mounted') : sprintf(_('See %s Settings'),ucfirst(prefix($tag)))?>
|
||||
|
||||
<?endif;?>
|
||||
<div class="title nocontrol"><span class="left"><i class="title fa fa-info"></i>_(Pool Information)_</span></div>
|
||||
<?exec("/usr/sbin/zpool list -v ".escapeshellarg($tag), $zfs_info_status, $info_retval); $zfs_info_status = implode("\n",$zfs_info_status)?>
|
||||
|
||||
_(zfs pool information)_:
|
||||
: <pre id='zfs-info'><?=$zfs_info_status?></pre>
|
||||
|
||||
</form>
|
||||
<hr>
|
||||
<?$scrub = str_replace('-','_',"scrub_$tag")?>
|
||||
<div class="title nocontrol"><span class="left"><i class="title fa fa-paint-brush"></i>_(Scrub Schedule)_</span></div>
|
||||
<form markdown="1" name="scrub_schedule" method="POST" action="/update.php" target="progressFrame" onsubmit="prepareZFS(this)">
|
||||
<input type="hidden" name="#file" value="dynamix/dynamix.cfg">
|
||||
<input type="hidden" name="#section" value="<?=$scrub?>">
|
||||
<input type="hidden" name="#include" value="/webGui/include/update.zfs.php">
|
||||
<input type="hidden" name="#job" value="scrub_<?=$tag?>;<?=$docroot?>/plugins/dynamix/scripts/zfs_scrub start <?=$tag?>">
|
||||
<input type="hidden" name="hour" value="">
|
||||
|
||||
_(Scrub schedule)_:
|
||||
: <select name="mode" onchange="presetZFS(this.form,'#scrub-hour')">
|
||||
<?for ($m=0; $m<count($mode); $m++):?>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?PHP
|
||||
<?php
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
@@ -9,89 +9,91 @@
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
|
||||
// add translations
|
||||
/* add translations */
|
||||
$_SERVER['REQUEST_URI'] = 'settings';
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
$map = $changes = [];
|
||||
|
||||
$data = json_decode($_POST['data'], true);
|
||||
|
||||
function decode($data) {
|
||||
return str_replace('%2e','.',urldecode($data));
|
||||
return str_replace('%2e', '.', urldecode($data));
|
||||
}
|
||||
foreach (array_map('decode',explode(';',$_POST['names'])) as $name) $map[$name] = '';
|
||||
foreach (array_map('decode', explode(';', $data['names'])) as $name) $map[$name] = '';
|
||||
|
||||
foreach($_POST as $key => $val) {
|
||||
if ($val != 'on') continue;
|
||||
[$name,$cpu] = my_explode(':',$key);
|
||||
$map[decode($name)] .= "$cpu,";
|
||||
foreach ($data['cpus'] as $key => $val) {
|
||||
if ($val != 'on') continue;
|
||||
[$name, $cpu] = my_explode(':', $key);
|
||||
$map[decode($name)] .= "$cpu,";
|
||||
}
|
||||
// map holds the list of each vm, container or isolcpus and its newly proposed cpu assignments
|
||||
$map = array_map(function($d){return substr($d,0,-1);},$map);
|
||||
/* map holds the list of each vm, container or isolcpus and its newly proposed cpu assignments */
|
||||
$map = array_map(function($d) {
|
||||
return substr($d, 0, -1);
|
||||
}, $map);
|
||||
|
||||
switch ($_POST['id']) {
|
||||
case 'vm':
|
||||
// report changed vms in temporary file
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
foreach ($map as $name => $cpuset) {
|
||||
if (!strlen($cpuset)) {
|
||||
$reply = ['error' => _("Not allowed to assign ZERO cores")];
|
||||
break 2;
|
||||
}
|
||||
$uuid = $lv->domain_get_uuid($lv->get_domain_by_name($name));
|
||||
$cfg = domain_to_config($uuid);
|
||||
$cpus = implode(',',$cfg['domain']['vcpu']);
|
||||
// only act on changes
|
||||
if ($cpus != $cpuset || strlen($cpus) != strlen($cpuset)) {
|
||||
$changes[] = $name;
|
||||
// used by UpdateTwo.php to read new assignments
|
||||
file_put_contents("/var/tmp/$name.tmp",$cpuset);
|
||||
}
|
||||
}
|
||||
$reply = ['success' => (count($changes) ? implode(';',$changes) : '')];
|
||||
break;
|
||||
case 'ct':
|
||||
// update the XML file of the container
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
$containers = $DockerClient->getDockerContainers();
|
||||
foreach ($map as $name => $cpuset) {
|
||||
// set full path of template file
|
||||
$file = $DockerTemplates->getUserTemplate($name);
|
||||
$xml = simplexml_load_file($file);
|
||||
if ($xml->CPUset) {
|
||||
// update node
|
||||
if ($xml->CPUset != $cpuset || strlen($xml->CPUset) != strlen($cpuset)) $xml->CPUset = $cpuset;
|
||||
} else {
|
||||
// add node
|
||||
$xml->addChild('CPUset',$cpuset);
|
||||
}
|
||||
// only act on changes
|
||||
foreach ($containers as $ct) if ($ct['Name']==$name) break;
|
||||
if ($ct['CPUset'] != $cpuset || strlen($ct['CPUset']) != strlen($cpuset)) {
|
||||
$changes[] = $name;
|
||||
// used by UpdateTwo.php to read new assignments
|
||||
file_put_contents($file,$xml->saveXML());
|
||||
exec("sed -ri 's/^(<CPUset)/ \\1/;s/><(\\/Container)/>\\n <\\1/' \"$file\""); // aftercare
|
||||
}
|
||||
}
|
||||
$reply = ['success' => (count($changes) ? implode(';',$changes) : '')];
|
||||
break;
|
||||
case 'is':
|
||||
// report changed isolcpus in temporary file
|
||||
foreach ($map as $name => $isolcpu) {
|
||||
file_put_contents("/var/tmp/$name.tmp",$isolcpu);
|
||||
$changes[] = $name;
|
||||
}
|
||||
$reply = ['success' => (count($changes) ? implode(';',$changes) : '')];
|
||||
break;
|
||||
switch ($data['id']) {
|
||||
case 'vm':
|
||||
/* report changed vms in temporary file */
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
foreach ($map as $name => $cpuset) {
|
||||
if (!strlen($cpuset)) {
|
||||
$reply = ['error' => _("Not allowed to assign ZERO cores")];
|
||||
break 2;
|
||||
}
|
||||
$uuid = $lv->domain_get_uuid($lv->get_domain_by_name($name));
|
||||
$cfg = domain_to_config($uuid);
|
||||
$cpus = implode(',', $cfg['domain']['vcpu']);
|
||||
/* only act on changes */
|
||||
if ($cpus != $cpuset || strlen($cpus) != strlen($cpuset)) {
|
||||
$changes[] = $name;
|
||||
/* used by UpdateTwo.php to read new assignments */
|
||||
file_put_contents_atomic("/var/tmp/$name.tmp", $cpuset);
|
||||
}
|
||||
}
|
||||
$reply = ['success' => (count($changes) ? implode(';', $changes) : '')];
|
||||
break;
|
||||
case 'ct':
|
||||
/* update the XML file of the container */
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
$containers = $DockerClient->getDockerContainers();
|
||||
foreach ($map as $name => $cpuset) {
|
||||
/* set full path of template file */
|
||||
$file = $DockerTemplates->getUserTemplate($name);
|
||||
$xml = simplexml_load_file($file);
|
||||
if ($xml->CPUset) {
|
||||
/* update node */
|
||||
if ($xml->CPUset != $cpuset || strlen($xml->CPUset) != strlen($cpuset)) $xml->CPUset = $cpuset;
|
||||
} else {
|
||||
/* add node */
|
||||
$xml->addChild('CPUset', $cpuset);
|
||||
}
|
||||
/* only act on changes */
|
||||
foreach ($containers as $ct) if ($ct['Name'] == $name) break;
|
||||
if ($ct['CPUset'] != $cpuset || strlen($ct['CPUset']) != strlen($cpuset)) {
|
||||
$changes[] = $name;
|
||||
/* used by UpdateTwo.php to read new assignments */
|
||||
file_put_contents_atomic($file, $xml->saveXML());
|
||||
exec("sed -ri 's/^(<CPUset)/ \\1/;s/><(\\/Container)/>\\n <\\1/' \"$file\""); /* aftercare */
|
||||
}
|
||||
}
|
||||
$reply = ['success' => (count($changes) ? implode(';', $changes) : '')];
|
||||
break;
|
||||
case 'is':
|
||||
/* report changed isolcpus in temporary file */
|
||||
foreach ($map as $name => $isolcpu) {
|
||||
file_put_contents_atomic("/var/tmp/$name.tmp", $isolcpu);
|
||||
$changes[] = $name;
|
||||
}
|
||||
$reply = ['success' => (count($changes) ? implode(';', $changes) : '')];
|
||||
break;
|
||||
}
|
||||
// signal changes
|
||||
/* signal changes */
|
||||
header('Content-Type: application/json');
|
||||
die(json_encode($reply));
|
||||
?>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?PHP
|
||||
<?php
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
@@ -13,166 +13,252 @@
|
||||
<?
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
|
||||
// add translations
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
|
||||
/* add translations */
|
||||
$_SERVER['REQUEST_URI'] = 'settings';
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
function scan($line, $text) {
|
||||
return stripos($line,$text)!==false;
|
||||
return stripos($line ?? '', $text) !== false;
|
||||
}
|
||||
|
||||
$reply = [];
|
||||
$name = urldecode($_POST['name']);
|
||||
switch ($_POST['id']) {
|
||||
case 'vm':
|
||||
// update vm
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
$file = "/var/tmp/$name.tmp";
|
||||
if (!file_exists($file)) {
|
||||
$reply = ['error' => "File: '$file' not found"];
|
||||
break;
|
||||
}
|
||||
// read new cpu assignments
|
||||
$cpuset = explode(',',file_get_contents($file)); unlink($file);
|
||||
$vcpus = count($cpuset);
|
||||
// initial cores/threads assignment
|
||||
$cores = $vcpus;
|
||||
$threads = 1;
|
||||
$ht = exec("lscpu|grep -Po '^Thread\\(s\\) per core:\\s+\\K\\d+'") ?: 1; // fetch hyperthreading
|
||||
/* Update VM */
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
|
||||
/* Path to the temporary file containing new CPU assignments */
|
||||
$file = "/var/tmp/$name.tmp";
|
||||
if (!file_exists($file)) {
|
||||
$reply = ['error' => "File: '$file' not found"];
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read new CPU assignments and delete the temporary file */
|
||||
$cpuset = explode(',', file_get_contents($file));
|
||||
unlink($file);
|
||||
$vcpus = count($cpuset);
|
||||
|
||||
/* Initial cores/threads assignment */
|
||||
$cores = $vcpus;
|
||||
$threads = 1;
|
||||
$ht = exec("lscpu|grep -Po '^Thread\\(s\\) per core:\\s+\\K\\d+'") ?: 1; /* Fetch hyperthreading */
|
||||
|
||||
/* Adjust for hyperthreading */
|
||||
if ($vcpus > $ht && $vcpus % $ht === 0) {
|
||||
$cores /= $ht;
|
||||
$threads = $ht;
|
||||
}
|
||||
|
||||
/* Get the UUID of the VM */
|
||||
$uuid = $lv->domain_get_uuid($lv->get_domain_by_name($name));
|
||||
$dom = $lv->domain_get_domain_by_uuid($uuid);
|
||||
$auto = $lv->domain_get_autostart($dom) == 1;
|
||||
|
||||
/* Load the VM's XML configuration */
|
||||
$xml = simplexml_load_string($lv->domain_get_xml($dom));
|
||||
|
||||
/* Update topology and vpcus in the XML configuration */
|
||||
$xml->cpu->topology['cores'] = $cores;
|
||||
$xml->cpu->topology['threads'] = $threads;
|
||||
$xml->vcpu = $vcpus;
|
||||
|
||||
/* Preserve existing emulatorpin attributes */
|
||||
$pin = [];
|
||||
foreach ($xml->cputune->emulatorpin->attributes() as $key => $value) {
|
||||
$pin[$key] = (string) $value;
|
||||
}
|
||||
unset($xml->cputune);
|
||||
|
||||
/* Add new cputune configuration */
|
||||
$xml->addChild('cputune');
|
||||
for ($i = 0; $i < $vcpus; $i++) {
|
||||
$vcpu = $xml->cputune->addChild('vcpupin');
|
||||
$vcpu['vcpu'] = $i;
|
||||
$vcpu['cpuset'] = _var($cpuset, $i);
|
||||
}
|
||||
if ($pin) {
|
||||
$attr = $xml->cputune->addChild('emulatorpin');
|
||||
foreach ($pin as $key => $value) {
|
||||
$attr[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop the running VM first if it is running */
|
||||
$running = $lv->domain_get_state($dom) == 'running';
|
||||
if ($running) {
|
||||
$lv->domain_shutdown($dom);
|
||||
for ($n = 0; $n < 30; $n++) { /* Allow up to 30s for VM to shutdown */
|
||||
sleep(1);
|
||||
if ($stopped = $lv->domain_get_state($dom) == 'shutoff') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$stopped = true;
|
||||
}
|
||||
|
||||
/* If the VM failed to stop, return an error */
|
||||
if (!$stopped) {
|
||||
$reply = ['error' => _('Failed to stop') . " '$name'"];
|
||||
break;
|
||||
}
|
||||
|
||||
/* Backup NVRAM, undefine the domain, and restore NVRAM */
|
||||
$lv->nvram_backup($uuid);
|
||||
$lv->domain_undefine($dom);
|
||||
$lv->nvram_restore($uuid);
|
||||
|
||||
/* Define the domain with the updated XML configuration */
|
||||
if (!$lv->domain_define($xml->saveXML())) {
|
||||
$reply = ['error' => $lv->get_last_error()];
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set autostart for the domain */
|
||||
$lv->domain_set_autostart($dom, $auto);
|
||||
|
||||
/* If the VM was running before, start it again */
|
||||
if ($running && !$lv->domain_start($dom)) {
|
||||
$reply = ['error' => $lv->get_last_error()];
|
||||
} else {
|
||||
$reply = ['success' => $name];
|
||||
}
|
||||
break;
|
||||
|
||||
// adjust for hyperthreading
|
||||
if ($vcpus > $ht && $vcpus%$ht===0) {
|
||||
$cores /= $ht;
|
||||
$threads = $ht;
|
||||
}
|
||||
$uuid = $lv->domain_get_uuid($lv->get_domain_by_name($name));
|
||||
$dom = $lv->domain_get_domain_by_uuid($uuid);
|
||||
$auto = $lv->domain_get_autostart($dom)==1;
|
||||
$xml = simplexml_load_string($lv->domain_get_xml($dom));
|
||||
// update topology and vpcus
|
||||
$xml->cpu->topology['cores'] = $cores;
|
||||
$xml->cpu->topology['threads'] = $threads;
|
||||
$xml->vcpu = $vcpus;
|
||||
$pin = []; foreach ($xml->cputune->emulatorpin->attributes() as $key => $value) $pin[$key] = (string)$value;
|
||||
unset($xml->cputune);
|
||||
$xml->addChild('cputune');
|
||||
for ($i = 0; $i < $vcpus; $i++) {
|
||||
$vcpu = $xml->cputune->addChild('vcpupin');
|
||||
$vcpu['vcpu'] = $i;
|
||||
$vcpu['cpuset'] = $cpuset[$i];
|
||||
}
|
||||
if ($pin) {
|
||||
$attr = $xml->cputune->addChild('emulatorpin');
|
||||
foreach ($pin as $key => $value) $attr[$key] = $value;
|
||||
}
|
||||
// stop running vm first?
|
||||
$running = $lv->domain_get_state($dom)=='running';
|
||||
if ($running) {
|
||||
$lv->domain_shutdown($dom);
|
||||
for ($n =0; $n < 30; $n++) { // allow up to 30s for VM to shutdown
|
||||
sleep(1);
|
||||
if ($stopped = $lv->domain_get_state($dom)=='shutoff') break;
|
||||
}
|
||||
} else $stopped = true;
|
||||
if (!$stopped) {
|
||||
$reply = ['error' => _('Failed to stop')." '$name'"];
|
||||
break;
|
||||
}
|
||||
$lv->nvram_backup($uuid);
|
||||
$lv->domain_undefine($dom);
|
||||
$lv->nvram_restore($uuid);
|
||||
if (!$lv->domain_define($xml->saveXML())) {
|
||||
$reply = ['error' => $lv->get_last_error()];
|
||||
break;
|
||||
}
|
||||
$lv->domain_set_autostart($dom, $auto);
|
||||
if ($running && !$lv->domain_start($dom)) {
|
||||
$reply = ['error' => $lv->get_last_error()];
|
||||
} else {
|
||||
$reply = ['success' => $name];
|
||||
}
|
||||
break;
|
||||
case 'ct':
|
||||
// update docker container
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
// get available networks
|
||||
$subnet = DockerUtil::network(DockerUtil::custom());
|
||||
// get full template path
|
||||
$xml = $DockerTemplates->getUserTemplate($name);
|
||||
list($cmd, $ct, $repository) = xmlToCommand($xml);
|
||||
$imageID = $DockerClient->getImageID($repository);
|
||||
// pull image
|
||||
$container = $DockerClient->getContainerDetails($ct);
|
||||
// determine if the container is still running
|
||||
if (!empty($container) && !empty($container['State']) && !empty($container['State']['Running'])) {
|
||||
// since container was already running, put it back it to a running state after update
|
||||
$cmd = str_replace('/docker create ', '/docker run -d ', $cmd);
|
||||
// attempt graceful stop of container first
|
||||
$DockerClient->stopContainer($ct);
|
||||
}
|
||||
// force kill container if still running after time-out
|
||||
$DockerClient->removeContainer($ct);
|
||||
execCommand($cmd,false);
|
||||
$DockerClient->flushCaches();
|
||||
$newImageID = $DockerClient->getImageID($repository);
|
||||
// remove old orphan image since it's no longer used by this container
|
||||
if ($imageID && $imageID != $newImageID) {
|
||||
$DockerClient->removeImage($imageID);
|
||||
}
|
||||
$reply = ['success' => $name];
|
||||
break;
|
||||
/* update docker container */
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
|
||||
/* get available networks */
|
||||
$subnet = DockerUtil::network(DockerUtil::custom());
|
||||
|
||||
/* get full template path */
|
||||
$xml = $DockerTemplates->getUserTemplate($name);
|
||||
list($cmd, $ct, $repository) = xmlToCommand($xml);
|
||||
$imageID = $DockerClient->getImageID($repository);
|
||||
|
||||
/* pull image */
|
||||
$container = $DockerClient->getContainerDetails($ct);
|
||||
|
||||
/* determine if the container is still running */
|
||||
if (!empty($container) && !empty($container['State']) && !empty($container['State']['Running'])) {
|
||||
/* since container was already running, put it back it to a running state after update */
|
||||
$cmd = str_replace('/docker create ', '/docker run -d ', $cmd);
|
||||
|
||||
/* attempt graceful stop of container first */
|
||||
$DockerClient->stopContainer($ct);
|
||||
}
|
||||
|
||||
/* force kill container if still running after time-out */
|
||||
$DockerClient->removeContainer($ct);
|
||||
execCommand($cmd, false);
|
||||
$DockerClient->flushCaches();
|
||||
$newImageID = $DockerClient->getImageID($repository);
|
||||
|
||||
/* remove old orphan image since it's no longer used by this container */
|
||||
if ($imageID && $imageID != $newImageID) {
|
||||
$DockerClient->removeImage($imageID);
|
||||
}
|
||||
$reply = ['success' => $name];
|
||||
break;
|
||||
|
||||
case 'is':
|
||||
$cfg = '/boot/syslinux/syslinux.cfg';
|
||||
$syslinux = file($cfg, FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
$size = count($syslinux);
|
||||
$make = false;
|
||||
$file = "/var/tmp/$name.tmp";
|
||||
$isolcpus = file_get_contents($file);
|
||||
if ($isolcpus != '') {
|
||||
$numbers = explode(',',$isolcpus);
|
||||
sort($numbers,SORT_NUMERIC);
|
||||
$isolcpus = $previous = array_shift($numbers);
|
||||
$range = false;
|
||||
// convert sequential numbers to a range
|
||||
foreach ($numbers as $number) {
|
||||
if ($number == $previous+1) {
|
||||
$range = true;
|
||||
} else {
|
||||
if ($range) {$isolcpus .= '-'.$previous; $range = false;}
|
||||
$isolcpus .= ','.$number;
|
||||
}
|
||||
$previous = $number;
|
||||
}
|
||||
if ($range) $isolcpus .= '-'.$previous;
|
||||
$isolcpus = "isolcpus=$isolcpus";
|
||||
}
|
||||
unlink($file);
|
||||
$i = 0;
|
||||
while ($i < $size) {
|
||||
// find sections and exclude safemode
|
||||
if (scan($syslinux[$i],'label ') && !scan($syslinux[$i],'safe mode') && !scan($syslinux[$i],'safemode')) {
|
||||
$n = $i + 1;
|
||||
// find the current requested setting
|
||||
while (!scan($syslinux[$n],'label ') && $n < $size) {
|
||||
if (scan($syslinux[$n],'append ')) {
|
||||
$cmd = preg_split('/\s+/',trim($syslinux[$n]));
|
||||
// replace an existing setting
|
||||
for ($c = 1; $c < count($cmd); $c++) if (scan($cmd[$c],'isolcpus')) {$make |= ($cmd[$c]!=$isolcpus); $cmd[$c] = $isolcpus; break;}
|
||||
// or insert a new setting
|
||||
if ($c==count($cmd) && $isolcpus) {array_splice($cmd,-1,0,$isolcpus); $make = true;}
|
||||
$syslinux[$n] = ' '.str_replace(' ',' ',implode(' ',$cmd));
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
$i = $n - 1;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
if ($make) file_put_contents($cfg, implode("\n",$syslinux)."\n");
|
||||
$reply = ['success' => $name];
|
||||
break;
|
||||
/* Path to syslinux configuration file */
|
||||
$cfg = '/boot/syslinux/syslinux.cfg';
|
||||
|
||||
/* Read the syslinux configuration file into an array, ignoring empty lines */
|
||||
$syslinux = file($cfg, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES);
|
||||
$size = count($syslinux);
|
||||
$make = false;
|
||||
|
||||
/* Path to the temporary file containing new isolcpus settings */
|
||||
$file = "/var/tmp/$name.tmp";
|
||||
$isolcpus = file_get_contents($file);
|
||||
|
||||
if ($isolcpus != '') {
|
||||
/* Convert isolcpus string to an array of numbers and sort them */
|
||||
$numbers = explode(',', $isolcpus);
|
||||
sort($numbers, SORT_NUMERIC);
|
||||
|
||||
/* Initialize variables for range conversion */
|
||||
$isolcpus = $previous = array_shift($numbers);
|
||||
$range = false;
|
||||
|
||||
/* Convert sequential numbers to a range */
|
||||
foreach ($numbers as $number) {
|
||||
if ($number == $previous + 1) {
|
||||
$range = true;
|
||||
} else {
|
||||
if ($range) {
|
||||
$isolcpus .= '-' . $previous;
|
||||
$range = false;
|
||||
}
|
||||
$isolcpus .= ',' . $number;
|
||||
}
|
||||
$previous = $number;
|
||||
}
|
||||
if ($range) {
|
||||
$isolcpus .= '-' . $previous;
|
||||
}
|
||||
|
||||
/* Format isolcpus string for syslinux configuration */
|
||||
$isolcpus = "isolcpus=$isolcpus";
|
||||
}
|
||||
|
||||
/* Remove the temporary file */
|
||||
unlink($file);
|
||||
|
||||
$i = 0;
|
||||
while ($i < $size) {
|
||||
/* Find sections in syslinux config and exclude safemode */
|
||||
if (scan($syslinux[$i], 'label ') && !scan($syslinux[$i], 'safe mode') && !scan($syslinux[$i], 'safemode')) {
|
||||
$n = $i + 1;
|
||||
|
||||
/* Find the current requested setting */
|
||||
while ($n < $size && !scan($syslinux[$n], 'label ')) {
|
||||
if (scan($syslinux[$n], 'append ')) {
|
||||
$cmd = preg_split('/\s+/', trim($syslinux[$n]));
|
||||
|
||||
/* Replace an existing isolcpus setting */
|
||||
for ($c = 1; $c < count($cmd); $c++) {
|
||||
if (scan($cmd[$c], 'isolcpus')) {
|
||||
$make |= ($cmd[$c] != $isolcpus);
|
||||
$cmd[$c] = $isolcpus;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Or insert a new isolcpus setting if not found */
|
||||
if ($c == count($cmd) && $isolcpus) {
|
||||
array_splice($cmd, -1, 0, $isolcpus);
|
||||
$make = true;
|
||||
}
|
||||
|
||||
/* Update the syslinux configuration line */
|
||||
$syslinux[$n] = ' ' . str_replace(' ', ' ', implode(' ', $cmd));
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
$i = $n - 1;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
/* Write the updated syslinux configuration back to the file if changes were made */
|
||||
if ($make) {
|
||||
file_put_contents_atomic($cfg, implode("\n", $syslinux) . "\n");
|
||||
}
|
||||
|
||||
$reply = ['success' => $name];
|
||||
break;
|
||||
}
|
||||
header('Content-Type: application/json');
|
||||
die(json_encode($reply));
|
||||
echo json_encode($reply);
|
||||
exit;
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user