mirror of
https://github.com/unraid/webgui.git
synced 2026-04-28 13:59:21 -05:00
Merge pull request #359 from bergware/master
Use timeout in command execution of diagnostics
This commit is contained in:
@@ -37,6 +37,7 @@ th.five{width:5%}
|
||||
th.eight{width:8%}
|
||||
th.load{width:100px}
|
||||
tbody > tr.sortable:hover{cursor:move}
|
||||
input.wait{width:24px;margin:0 4px;padding:0 5px;border:none;box-shadow:none;background-color:transparent}
|
||||
</style>
|
||||
<div id="dialog-confirm" style="display:none;" title="Dialog Title"></div>
|
||||
<div id="iframe-popup" style="display:none;-webkit-overflow-scrolling:touch;"></div>
|
||||
@@ -75,11 +76,17 @@ function resetSorting() {
|
||||
$.post('/plugins/dynamix.docker.manager/include/UserPrefs.php',{reset:true},function(){loadlist();});
|
||||
}
|
||||
function listview() {
|
||||
if ($.cookie('docker_listview_mode')=='advanced') {
|
||||
var more = $.cookie('docker_listview_mode')=='advanced';
|
||||
if (more) {
|
||||
$('.docker_readmore').readmore('destroy');
|
||||
} else {
|
||||
$('.docker_readmore').readmore({maxHeight:32,moreLink:"<a href='#' style='text-align:center'><i class='fa fa-chevron-down'></i></a>",lessLink:"<a href='#' style='text-align:center'><i class='fa fa-chevron-up'></i></a>"});
|
||||
}
|
||||
$('input.autostart').each(function(){
|
||||
var wait = $('#'+$(this).prop('id').replace('auto','wait'));
|
||||
var auto = $(this).prop('checked');
|
||||
if (auto && more) wait.show(); else wait.hide();
|
||||
});
|
||||
}
|
||||
var sortableHelper = function(e,i){
|
||||
i.children().each(function(){
|
||||
@@ -87,7 +94,7 @@ var sortableHelper = function(e,i){
|
||||
});
|
||||
return i;
|
||||
};
|
||||
function loadlist(update) {
|
||||
function loadlist() {
|
||||
$.get('/plugins/dynamix.docker.manager/include/DockerContainers.php',function(d) {
|
||||
var data = d.split(/\0/);
|
||||
$('div.spinner').hide('slow');
|
||||
@@ -105,19 +112,28 @@ function loadlist(update) {
|
||||
$('.iconstatus').each(function(){
|
||||
if ($(this).hasClass('stopped')) $('div.'+$(this).prop('id')).hide();
|
||||
});
|
||||
$('.autostart').switchButton({labels_placement:"right"});
|
||||
$('.autostart').change(function() {
|
||||
$.post( "/plugins/dynamix.docker.manager/include/UpdateConfig.php",{action:'autostart',container:$(this).attr('container'),response:'json'},function(data){$(this).prop('checked',data.autostart);},'json');
|
||||
$('.autostart').switchButton({labels_placement:'right'});
|
||||
$('.autostart').change(function(){
|
||||
var more = $.cookie('docker_listview_mode')=='advanced';
|
||||
var wait = $('#'+$(this).prop('id').replace('auto','wait'));
|
||||
var auto = $(this).prop('checked');
|
||||
if (auto && more) wait.show(); else wait.hide();
|
||||
$.post('/plugins/dynamix.docker.manager/include/UpdateConfig.php',{action:'autostart',container:$(this).attr('container'),auto:auto,wait:wait.find('input.wait').val()});
|
||||
});
|
||||
$('input.wait').change(function(){
|
||||
$.post('/plugins/dynamix.docker.manager/include/UpdateConfig.php',{action:'wait',container:$(this).attr('container'),wait:$(this).val()});
|
||||
});
|
||||
if ($.cookie('docker_listview_mode')=='advanced') {$('.advanced').show(); $('.basic').hide();}
|
||||
listview();
|
||||
context.init({preventDoubleContext:false});
|
||||
$('input[type=button]').prop('disabled',false).show('slow');
|
||||
var update = false;
|
||||
for (var i=0,ct; ct=docker[i]; i++) if (ct.update=='false') {update = true; break;};
|
||||
if (!update) $('input#updateAll').hide();
|
||||
listview();
|
||||
});
|
||||
}
|
||||
var watchDocker = new NchanSubscriber('/sub/dockerload', /^((?!chrome|android).)*safari/i.test(navigator.userAgent) ? {subscriber:'longpoll'} : {});
|
||||
watchDocker.on('message', function(data) {
|
||||
watchDocker.on('message', function(data){
|
||||
data = data.split('\n');
|
||||
for (var i=0,row; row=data[i]; i++) {
|
||||
var id = row.split(' ');
|
||||
@@ -127,13 +143,13 @@ watchDocker.on('message', function(data) {
|
||||
});
|
||||
$(function() {
|
||||
$('.advancedview').switchButton({labels_placement:'left', on_label:'Advanced View', off_label:'Basic View', checked:$.cookie('docker_listview_mode')=='advanced'});
|
||||
$('.advancedview').change(function() {
|
||||
$('.advancedview').change(function(){
|
||||
$('.advanced').toggle('slow');
|
||||
$('.basic').toggle('slow');
|
||||
$.cookie('docker_listview_mode',$('.advancedview').is(':checked')?'advanced':'basic',{expires:3650});
|
||||
listview();
|
||||
});
|
||||
$.post('/plugins/dynamix.docker.manager/include/DockerUpdate.php',{},function(u){loadlist(u);});
|
||||
loadlist();
|
||||
watchDocker.start();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/Helpers.php";
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
|
||||
$var = parse_ini_file('state/var.ini');
|
||||
ignore_user_abort(true);
|
||||
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerUpdate = new DockerUpdate();
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerUpdate = new DockerUpdate();
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
|
||||
# ███████╗██╗ ██╗███╗ ██╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗███████╗
|
||||
@@ -30,12 +31,31 @@ $DockerTemplates = new DockerTemplates();
|
||||
# ██║ ╚██████╔╝██║ ╚████║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║███████║
|
||||
# ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝
|
||||
|
||||
$custom = DockerUtil::docker("network ls --filter driver='bridge' --filter driver='macvlan' --format='{{.Name}}'|grep -v '^bridge$'",true);
|
||||
$subnet = ['bridge'=>'', 'host'=>'', 'none'=>''];
|
||||
|
||||
foreach ($custom as $network) $subnet[$network] = substr(DockerUtil::docker("network inspect --format='{{range .IPAM.Config}}{{.Subnet}}, {{end}}' $network"),0,-1);
|
||||
|
||||
$custom = DockerUtil::custom();
|
||||
$subnet = DockerUtil::network($custom);
|
||||
$cpus = cpu_list();
|
||||
|
||||
function cpu_pinning() {
|
||||
global $xml,$cpus;
|
||||
$vcpu = explode(',',$xml['CPUset'] ?? '');
|
||||
$total = count($cpus);
|
||||
$loop = floor(($total-1)/22)+1;
|
||||
for ($c = 0; $c < $loop; $c++) {
|
||||
$row1 = $row2 = [];
|
||||
$max = ($c == $loop-1 ? ($total%22?:22) : 22);
|
||||
for ($n = 0; $n < $max; $n++) {
|
||||
unset($cpu1,$cpu2);
|
||||
list($cpu1, $cpu2) = preg_split('/[,-]/',$cpus[$c*22+$n]);
|
||||
$check1 = in_array($cpu1, $vcpu) ? ' checked':'';
|
||||
$check2 = $cpu2 ? (in_array($cpu2, $vcpu) ? ' checked':''):'';
|
||||
$row1[] = "<span id='cpu$cpu1' class='cpu'><input type='checkbox' id='box$cpu1'$check1>$cpu1</span>";
|
||||
if ($cpu2) $row2[] = "<span id='cpu$cpu2' class='cpu'><input type='checkbox' id='box$cpu2'$check2>$cpu2</span>";
|
||||
}
|
||||
if ($c) echo '<hr>';
|
||||
echo "<span class='cpu'>CPU:</span>".implode($row1);
|
||||
if ($row2) echo "<br><span class='cpu'>HT:</span>".implode($row2);
|
||||
}
|
||||
}
|
||||
|
||||
# ██████╗ ██████╗ ██████╗ ███████╗
|
||||
# ██╔════╝██╔═══██╗██╔══██╗██╔════╝
|
||||
@@ -251,6 +271,7 @@ optgroup.title{background-color:#625D5D;color:#FFFFFF;text-align:center;margin-t
|
||||
.switch-button-label.off{color:inherit;}
|
||||
.selectVariable{width:320px}
|
||||
.fa.button{color:maroon;font-size:24px;position:relative;top:4px;cursor:pointer}
|
||||
span.cpu{display:inline-block;width:50px}
|
||||
</style>
|
||||
<script src="<?autov('/webGui/javascript/jquery.switchbutton.js')?>"></script>
|
||||
<script src="<?autov('/webGui/javascript/jquery.filetree.js')?>"></script>
|
||||
@@ -524,6 +545,9 @@ optgroup.title{background-color:#625D5D;color:#FFFFFF;text-align:center;margin-t
|
||||
$(form).find('input[name="confTarget[]"]').each(function(){targets.push($(this));});
|
||||
for (var i=0; i < types.length; i++) if (types[i]=='Port') $(targets[i]).val($(values[i]).val());
|
||||
}
|
||||
var vcpu = [];
|
||||
$(form).find('input[id^="box"]').each(function(){if ($(this).prop('checked')) vcpu.push($('#'+$(this).prop('id').replace('box','cpu')).text());});
|
||||
form.contCPUset.value = vcpu.join(',');
|
||||
}
|
||||
|
||||
function makeName(type) {
|
||||
@@ -664,6 +688,7 @@ optgroup.title{background-color:#625D5D;color:#FFFFFF;text-align:center;margin-t
|
||||
<div id="canvas">
|
||||
<form method="POST" autocomplete="off" onsubmit="prepareConfig(this)">
|
||||
<input type="hidden" name="csrf_token" value="<?=$var['csrf_token']?>">
|
||||
<input type="hidden" name="contCPUset" value="">
|
||||
<table class="settings">
|
||||
<? if ($xmlType == 'edit'):
|
||||
if ($DockerClient->doesContainerExist($templateName)): echo "<input type='hidden' name='existingContainer' value='${templateName}'>\n"; endif;
|
||||
@@ -902,8 +927,6 @@ optgroup.title{background-color:#625D5D;color:#FFFFFF;text-align:center;margin-t
|
||||
<td colspan="2">
|
||||
<blockquote class="inline_help">
|
||||
<p>If you wish to append additional commands to your Docker container at run-time, you can specify them here.<br>
|
||||
For example, if you wish to pin an application to live on a specific CPU core, you can enter "--cpuset=0" in this field.
|
||||
Change 0 to the core # on your system (starting with 0). You can pin multiple cores by separation with a comma or a range of cores by separation with a dash.
|
||||
For all possible Docker run-time commands, see here: <a href="https://docs.docker.com/reference/run/" target="_blank">https://docs.docker.com/reference/run/</a></p>
|
||||
</blockquote>
|
||||
</td>
|
||||
@@ -920,6 +943,20 @@ optgroup.title{background-color:#625D5D;color:#FFFFFF;text-align:center;margin-t
|
||||
</blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="advanced">
|
||||
<td>CPU Pinning:</td>
|
||||
<td><?cpu_pinning()?></td>
|
||||
</tr>
|
||||
<tr class="advanced">
|
||||
<td colspan="2">
|
||||
<blockquote class="inline_help">
|
||||
<p>Help text for CPU pinning.</p>
|
||||
</blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr <?=$showAdditionalInfo?>>
|
||||
<td>Network Type:</td>
|
||||
<td>
|
||||
|
||||
@@ -38,6 +38,10 @@ $driver = DockerUtil::driver();
|
||||
$docker_cfgfile = '/boot/config/docker.cfg';
|
||||
$dockercfg = parse_ini_file($docker_cfgfile);
|
||||
|
||||
function var_split($item, $i=0) {
|
||||
return explode(' ',$item)[$i];
|
||||
}
|
||||
|
||||
#######################################
|
||||
## DOCKERTEMPLATES CLASS ##
|
||||
#######################################
|
||||
@@ -257,7 +261,7 @@ class DockerTemplates {
|
||||
$DockerUpdate = new DockerUpdate();
|
||||
//$DockerUpdate->verbose = $this->verbose;
|
||||
$info = DockerUtil::loadJSON($dockerManPaths['webui-info']);
|
||||
$autoStart = @file($dockerManPaths['autostart-file'], FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$autoStart = array_map('var_split', @file($dockerManPaths['autostart-file'], FILE_IGNORE_NEW_LINES) ?: []);
|
||||
foreach ($DockerClient->getDockerContainers() as $ct) {
|
||||
$name = $ct['Name'];
|
||||
$image = $ct['Image'];
|
||||
@@ -265,6 +269,7 @@ class DockerTemplates {
|
||||
$tmp['running'] = $ct['Running'];
|
||||
$tmp['paused'] = $ct['Paused'];
|
||||
$tmp['autostart'] = in_array($name, $autoStart);
|
||||
$tmp['cpuset'] = $ct['CPUset'];
|
||||
if (!is_file($tmp['icon']) || $reload) $tmp['icon'] = $this->getIcon($image);
|
||||
if ($ct['Running']) {
|
||||
$port = &$ct['Ports'][0];
|
||||
@@ -575,13 +580,14 @@ class DockerClient {
|
||||
}
|
||||
|
||||
public function getDockerJSON($url, $method='GET', &$code=null, $callback=null, $unchunk=false) {
|
||||
$api = '/v1.37'; // used to force an API version. See https://docs.docker.com/develop/sdk/#api-version-matrix
|
||||
$fp = stream_socket_client('unix:///var/run/docker.sock', $errno, $errstr);
|
||||
if ($fp === false) {
|
||||
echo "Couldn't create socket: [$errno] $errstr";
|
||||
return null;
|
||||
}
|
||||
$protocol = $unchunk ? 'HTTP/1.0' : 'HTTP/1.1';
|
||||
$out = "$method $url $protocol\r\nHost:127.0.0.1\r\nConnection:Close\r\n\r\n";
|
||||
$out = "$method {$api}{$url} $protocol\r\nHost:127.0.0.1\r\nConnection:Close\r\n\r\n";
|
||||
fwrite($fp, $out);
|
||||
// Strip headers out
|
||||
$headers = '';
|
||||
@@ -639,8 +645,8 @@ class DockerClient {
|
||||
return $code;
|
||||
}
|
||||
|
||||
public function stopContainer($id) {
|
||||
$this->getDockerJSON("/containers/$id/stop", 'POST', $code);
|
||||
public function stopContainer($id, $t=10) {
|
||||
$this->getDockerJSON("/containers/$id/stop?t=$t", 'POST', $code);
|
||||
$this->flushCache($this::$containersCache);
|
||||
return $code;
|
||||
}
|
||||
@@ -657,7 +663,7 @@ class DockerClient {
|
||||
return $code;
|
||||
}
|
||||
|
||||
public function removeContainer($name, $id, $cache=false) {
|
||||
public function removeContainer($name, $id=false, $cache=false) {
|
||||
global $docroot, $dockerManPaths;
|
||||
$id = $id ?: $name;
|
||||
$info = DockerUtil::loadJSON($dockerManPaths['webui-info']);
|
||||
@@ -720,6 +726,7 @@ class DockerClient {
|
||||
$c['Volumes'] = $info['HostConfig']['Binds'];
|
||||
$c['Created'] = $this->humanTiming($ct['Created']);
|
||||
$c['NetworkMode'] = $ct['HostConfig']['NetworkMode'];
|
||||
$c['CPUset'] = $info['HostConfig']['CpusetCpus'];
|
||||
$c['BaseImage'] = $ct['Labels']['BASEIMAGE'] ?? false;
|
||||
$c['Ports'] = [];
|
||||
if ($driver[$c['NetworkMode']]=='bridge') {
|
||||
@@ -829,5 +836,13 @@ class DockerUtil {
|
||||
foreach (static::docker("network ls --format='{{.Name}}={{.Driver}}'",true) as $network) {list($name,$driver) = explode('=',$network); $list[$name] = $driver;}
|
||||
return $list;
|
||||
}
|
||||
public static function custom() {
|
||||
return static::docker("network ls --filter driver='bridge' --filter driver='macvlan' --format='{{.Name}}'|grep -v '^bridge$'",true);
|
||||
}
|
||||
public static function network($more) {
|
||||
$list = ['bridge'=>'', 'host'=>'', 'none'=>''];
|
||||
foreach ($more as $net) $list[$net] = substr(static::docker("network inspect --format='{{range .IPAM.Config}}{{.Subnet}}, {{end}}' $net"),0,-1);
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -20,6 +20,7 @@ $DockerTemplates = new DockerTemplates();
|
||||
$containers = $DockerClient->getDockerContainers();
|
||||
$images = $DockerClient->getDockerImages();
|
||||
$user_prefs = $dockerManPaths['user-prefs'];
|
||||
$autostart_file = $dockerManPaths['autostart-file'];
|
||||
|
||||
if (!$containers && !$images) {
|
||||
echo "<tr><td colspan='8' style='text-align:center;padding-top:12px'>No Docker containers installed</td></tr>";
|
||||
@@ -39,6 +40,9 @@ $docker = ['var docker=[];'];
|
||||
$null = '0.0.0.0';
|
||||
$menu = [];
|
||||
|
||||
$autostart = @file($autostart_file, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$names = array_map('var_split', $autostart);
|
||||
|
||||
foreach ($containers as $ct) {
|
||||
$name = $ct['Name'];
|
||||
$id = $ct['Id'];
|
||||
@@ -57,6 +61,7 @@ foreach ($containers as $ct) {
|
||||
$shape = $running ? ($paused ? 'pause' : 'play') : 'square';
|
||||
$status = $running ? ($paused ? 'paused' : 'started') : 'stopped';
|
||||
$icon = $info['icon'] ?: '/plugins/dynamix.docker.manager/images/question.png';
|
||||
$wait = var_split($autostart[array_search($name,$names)],1);
|
||||
$ports = [];
|
||||
foreach ($ct['Ports'] as $port) {
|
||||
$intern = $running ? ($ct['NetworkMode']=='host' ? $host : $port['IP']) : $null;
|
||||
@@ -102,7 +107,8 @@ foreach ($containers as $ct) {
|
||||
echo "<td style='word-break:break-all'><span class='docker_readmore'>".implode('<br>',$paths)."</span></td>";
|
||||
echo "<td class='advanced'><div class='usage-disk sys load-$id'><span id='cpu-$id' style='width:0'></span></div></td>";
|
||||
echo "<td class='advanced'><div class='usage-disk sys load-$id'><span id='mem-$id' style='width:0'></span></div></td>";
|
||||
echo "<td><input type='checkbox' class='autostart' container='".htmlspecialchars($name)."'".($info['autostart'] ? ' checked':'')."></td>";
|
||||
echo "<td><input type='checkbox' id='$id-auto' class='autostart' container='".htmlspecialchars($name)."'".($info['autostart'] ? ' checked':'').">";
|
||||
echo "<span id='$id-wait' style='float:right;display:none'>wait<input class='wait' container='".htmlspecialchars($name)."' type='number' value='$wait' placeholder='0' title='seconds'></span></td>";
|
||||
echo "<td><a class='log' onclick=\"containerLogs('".addslashes(htmlspecialchars($name))."','$id',false,false)\"><img class='basic' src='/plugins/dynamix/icons/log.png'><div class='advanced'>";
|
||||
echo htmlspecialchars(str_replace('Up','Uptime',$ct['Status']))."</div><div class='advanced' style='margin-top:4px'>Created ".htmlspecialchars($ct['Created'])."</div></a></td></tr>";
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2018, Lime Technology
|
||||
* Copyright 2014-2018, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2018, 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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
|
||||
if ($_POST['check']) {
|
||||
$DockerTemplates->downloadTemplates();
|
||||
$DockerTemplates->getAllInfo(true);
|
||||
}
|
||||
foreach ($DockerTemplates->getAllInfo() as $info) {
|
||||
if ($info['updated']=='false' && $info['updated']!='undef') {echo 'true'; break;}
|
||||
}
|
||||
?>
|
||||
@@ -29,6 +29,7 @@ function postToXML($post, $setOwnership=false) {
|
||||
$xml->Icon = xml_encode(trim($post['contIcon']));
|
||||
$xml->ExtraParams = xml_encode($post['contExtraParams']);
|
||||
$xml->PostArgs = xml_encode($post['contPostArgs']);
|
||||
$xml->CPUset = xml_encode($post['contCPUset']);
|
||||
$xml->DateInstalled = xml_encode(time());
|
||||
$xml->DonateText = xml_encode($post['contDonateText']);
|
||||
$xml->DonateLink = xml_encode($post['contDonateLink']);
|
||||
@@ -104,6 +105,7 @@ function xmlToVar($xml) {
|
||||
$out['Icon'] = xml_decode($xml->Icon);
|
||||
$out['ExtraParams'] = xml_decode($xml->ExtraParams);
|
||||
$out['PostArgs'] = xml_decode($xml->PostArgs);
|
||||
$out['CPUset'] = xml_decode($xml->CPUset);
|
||||
$out['DonateText'] = xml_decode($xml->DonateText);
|
||||
$out['DonateLink'] = xml_decode($xml->DonateLink);
|
||||
$out['Config'] = [];
|
||||
@@ -246,6 +248,7 @@ function xmlToCommand($xml, $create_paths=false) {
|
||||
$cmdPrivileged = strtolower($xml['Privileged'])=='true' ? '--privileged=true' : '';
|
||||
$cmdNetwork = '--net='.escapeshellarg(strtolower($xml['Network']));
|
||||
$cmdMyIP = $xml['MyIP'] ? '--ip='.escapeshellarg($xml['MyIP']) : '';
|
||||
$cmdCPUset = strlen($xml['CPUset']) ? '--cpuset-cpus='.escapeshellarg($xml['CPUset']) : '';
|
||||
$Volumes = [''];
|
||||
$Ports = [''];
|
||||
$Variables = [''];
|
||||
@@ -292,57 +295,71 @@ function xmlToCommand($xml, $create_paths=false) {
|
||||
}
|
||||
}
|
||||
|
||||
$cmd = sprintf($docroot.'/plugins/dynamix.docker.manager/scripts/docker create %s %s %s %s %s %s %s %s %s %s %s %s',
|
||||
$cmdName, $cmdNetwork, $cmdMyIP, $cmdPrivileged, implode(' -e ', $Variables), implode(' -l ', $Labels), implode(' -p ', $Ports), implode(' -v ', $Volumes), implode(' --device=', $Devices), $xml['ExtraParams'], escapeshellarg($xml['Repository']), $xml['PostArgs']);
|
||||
$cmd = sprintf($docroot.'/plugins/dynamix.docker.manager/scripts/docker create %s %s %s %s %s %s %s %s %s %s %s %s %s',
|
||||
$cmdName, $cmdNetwork, $cmdMyIP, $cmdCPUset, $cmdPrivileged, implode(' -e ', $Variables), implode(' -l ', $Labels), implode(' -p ', $Ports), implode(' -v ', $Volumes), implode(' --device=', $Devices), $xml['ExtraParams'], escapeshellarg($xml['Repository']), $xml['PostArgs']);
|
||||
return [preg_replace('/\s+/', ' ', $cmd), $xml['Name'], $xml['Repository']];
|
||||
}
|
||||
function stopContainer($name) {
|
||||
function stopContainer($name, $t=10, $echo=true) {
|
||||
global $DockerClient;
|
||||
$waitID = mt_rand();
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Stopping container: ".addslashes(htmlspecialchars($name))."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
$retval = $DockerClient->stopContainer($name);
|
||||
if ($echo) {
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Stopping container: ".addslashes(htmlspecialchars($name))."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
}
|
||||
$retval = $DockerClient->stopContainer($name, $t);
|
||||
$out = ($retval === true) ? "Successfully stopped container '$name'" : "Error: ".$retval;
|
||||
echo "<script>stop_Wait($waitID);addLog('<b>".addslashes(htmlspecialchars($out))."</b>');</script>\n";
|
||||
@flush();
|
||||
if ($echo) {
|
||||
echo "<script>stop_Wait($waitID);addLog('<b>".addslashes(htmlspecialchars($out))."</b>');</script>\n";
|
||||
@flush();
|
||||
}
|
||||
}
|
||||
|
||||
function removeContainer($name, $cache=false) {
|
||||
function removeContainer($name, $cache=false, $echo=true) {
|
||||
global $DockerClient;
|
||||
$waitID = mt_rand();
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Removing container: ".addslashes(htmlspecialchars($name))."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
if ($echo) {
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Removing container: ".addslashes(htmlspecialchars($name))."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
}
|
||||
$retval = $DockerClient->removeContainer($name, false, $cache);
|
||||
$out = ($retval === true) ? "Successfully removed container '$name'" : "Error: ".$retval;
|
||||
echo "<script>stop_Wait($waitID);addLog('<b>".addslashes(htmlspecialchars($out))."</b>');</script>\n";
|
||||
@flush();
|
||||
if ($echo) {
|
||||
echo "<script>stop_Wait($waitID);addLog('<b>".addslashes(htmlspecialchars($out))."</b>');</script>\n";
|
||||
@flush();
|
||||
}
|
||||
}
|
||||
|
||||
function removeImage($image) {
|
||||
function removeImage($image, $echo=true) {
|
||||
global $DockerClient;
|
||||
$waitID = mt_rand();
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Removing orphan image: ".addslashes(htmlspecialchars($image))."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
if ($echo) {
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Removing orphan image: ".addslashes(htmlspecialchars($image))."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
}
|
||||
$retval = $DockerClient->removeImage($image);
|
||||
$out = ($retval === true) ? "Successfully removed image '$image'" : "Error: ".$retval;
|
||||
echo "<script>stop_Wait($waitID);addLog('<b>".addslashes(htmlspecialchars($out))."</b>');</script>\n";
|
||||
@flush();
|
||||
if ($echo) {
|
||||
echo "<script>stop_Wait($waitID);addLog('<b>".addslashes(htmlspecialchars($out))."</b>');</script>\n";
|
||||
@flush();
|
||||
}
|
||||
}
|
||||
|
||||
function pullImage($name, $image) {
|
||||
function pullImage($name, $image, $echo=true) {
|
||||
global $DockerClient, $DockerTemplates, $DockerUpdate;
|
||||
$waitID = mt_rand();
|
||||
if (!preg_match("/:\S+$/", $image)) $image .= ":latest";
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Pulling image: ".addslashes(htmlspecialchars($image))."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
if ($echo) {
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Pulling image: ".addslashes(htmlspecialchars($image))."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
}
|
||||
$alltotals = [];
|
||||
$laststatus = [];
|
||||
$strError = '';
|
||||
$DockerClient->pullImage($image, function ($line) use (&$alltotals, &$laststatus, &$waitID, &$strError, $image, $DockerClient, $DockerUpdate) {
|
||||
$DockerClient->pullImage($image, function ($line) use (&$alltotals, &$laststatus, &$waitID, &$strError, $image, $DockerClient, $DockerUpdate, $echo) {
|
||||
$cnt = json_decode($line, true);
|
||||
$id = (isset($cnt['id'])) ? trim($cnt['id']) : '';
|
||||
$status = (isset($cnt['status'])) ? trim($cnt['status']) : '';
|
||||
@@ -350,8 +367,10 @@ function pullImage($name, $image) {
|
||||
$strError = $cnt['error'];
|
||||
}
|
||||
if ($waitID !== false) {
|
||||
echo "<script>stop_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
if ($echo) {
|
||||
echo "<script>stop_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
}
|
||||
$waitID = false;
|
||||
}
|
||||
if (empty($status)) return;
|
||||
@@ -368,51 +387,55 @@ function pullImage($name, $image) {
|
||||
break;
|
||||
case 'Downloading':
|
||||
if ($laststatus[$id] != $status) {
|
||||
echo "<script>addToID('${id}','".addslashes(htmlspecialchars($status))."');</script>\n";
|
||||
if ($echo) echo "<script>addToID('${id}','".addslashes(htmlspecialchars($status))."');</script>\n";
|
||||
}
|
||||
$total = $cnt['progressDetail']['total'];
|
||||
$current = $cnt['progressDetail']['current'];
|
||||
if ($total > 0) {
|
||||
$percentage = round(($current / $total) * 100);
|
||||
echo "<script>progress('${id}',' ".$percentage."% of ".$DockerClient->formatBytes($total)."');</script>\n";
|
||||
if ($echo) echo "<script>progress('${id}',' ".$percentage."% of ".$DockerClient->formatBytes($total)."');</script>\n";
|
||||
} else {
|
||||
// Docker must not know the total download size (http-chunked or something?)
|
||||
// just show the current download progress without the percentage
|
||||
$alltotals[$id] = $current;
|
||||
echo "<script>progress('${id}',' ".$DockerClient->formatBytes($current)."');</script>\n";
|
||||
if ($echo) echo "<script>progress('${id}',' ".$DockerClient->formatBytes($current)."');</script>\n";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($laststatus[$id] == "Downloading") {
|
||||
echo "<script>progress('${id}',' 100% of ".$DockerClient->formatBytes($alltotals[$id])."');</script>\n";
|
||||
if ($echo) echo "<script>progress('${id}',' 100% of ".$DockerClient->formatBytes($alltotals[$id])."');</script>\n";
|
||||
}
|
||||
if ($laststatus[$id] != $status) {
|
||||
echo "<script>addToID('${id}','".addslashes(htmlspecialchars($status))."');</script>\n";
|
||||
if ($echo) echo "<script>addToID('${id}','".addslashes(htmlspecialchars($status))."');</script>\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
$laststatus[$id] = $status;
|
||||
} else {
|
||||
if (strpos($status, 'Status: ') === 0) {
|
||||
echo "<script>addLog('".addslashes(htmlspecialchars($status))."');</script>\n";
|
||||
if ($echo) echo "<script>addLog('".addslashes(htmlspecialchars($status))."');</script>\n";
|
||||
}
|
||||
if (strpos($status, 'Digest: ') === 0) {
|
||||
$DockerUpdate->setUpdateStatus($image, substr($status, 8));
|
||||
}
|
||||
}
|
||||
@flush();
|
||||
if ($echo) @flush();
|
||||
});
|
||||
echo "<script>addLog('<br><b>TOTAL DATA PULLED:</b> " . $DockerClient->formatBytes(array_sum($alltotals)) . "');</script>\n";
|
||||
@flush();
|
||||
if (!empty($strError)) {
|
||||
echo "<script>addLog('<br><span class=\"error\"><b>Error:</b> ".addslashes(htmlspecialchars($strError))."</span>');</script>\n";
|
||||
if ($echo) {
|
||||
echo "<script>addLog('<br><b>TOTAL DATA PULLED:</b> " . $DockerClient->formatBytes(array_sum($alltotals)) . "');</script>\n";
|
||||
@flush();
|
||||
}
|
||||
if (!empty($strError)) {
|
||||
if ($echo) {
|
||||
echo "<script>addLog('<br><span class=\"error\"><b>Error:</b> ".addslashes(htmlspecialchars($strError))."</span>');</script>\n";
|
||||
@flush();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function execCommand($command) {
|
||||
function execCommand($command, $echo=true) {
|
||||
if ( dockerRunSecurity($command) ) {
|
||||
$command = "logger 'docker command execution halted due to security violation (Bash command execution or redirection)'";
|
||||
}
|
||||
@@ -423,22 +446,26 @@ function execCommand($command) {
|
||||
2 => ['pipe', 'w'] // stderr is a pipe that the child will write to
|
||||
];
|
||||
$id = mt_rand();
|
||||
echo '<p class="logLine" id="logBody"></p>';
|
||||
echo '<script>addLog(\'<fieldset style="margin-top:1px;" class="CMD"><legend>Command:</legend>';
|
||||
echo 'root@localhost:# '.addslashes(htmlspecialchars($command)).'<br>';
|
||||
echo '<span id="wait'.$id.'">Please wait </span>';
|
||||
echo '<p class="logLine" id="logBody"></p></fieldset>\');show_Wait('.$id.');</script>';
|
||||
@flush();
|
||||
if ($echo) {
|
||||
echo '<p class="logLine" id="logBody"></p>';
|
||||
echo '<script>addLog(\'<fieldset style="margin-top:1px;" class="CMD"><legend>Command:</legend>';
|
||||
echo 'root@localhost:# '.addslashes(htmlspecialchars($command)).'<br>';
|
||||
echo '<span id="wait'.$id.'">Please wait </span>';
|
||||
echo '<p class="logLine" id="logBody"></p></fieldset>\');show_Wait('.$id.');</script>';
|
||||
@flush();
|
||||
}
|
||||
$proc = proc_open($command." 2>&1", $descriptorspec, $pipes, '/', []);
|
||||
while ($out = fgets( $pipes[1] )) {
|
||||
$out = preg_replace("%[\t\n\x0B\f\r]+%", '', $out);
|
||||
echo '<script>addLog("'.htmlspecialchars($out).'");</script>';
|
||||
@flush();
|
||||
if ($echo) {
|
||||
echo '<script>addLog("'.htmlspecialchars($out).'");</script>';
|
||||
@flush();
|
||||
}
|
||||
}
|
||||
$retval = proc_close($proc);
|
||||
echo '<script>stop_Wait('.$id.');</script>';
|
||||
if ($echo) echo '<script>stop_Wait('.$id.');</script>';
|
||||
$out = $retval ? 'The command failed.' : 'The command finished successfully!';
|
||||
echo '<script>addLog(\'<br><b>'.$out.'</b>\');</script>';
|
||||
if ($echo) echo '<script>addLog(\'<br><b>'.$out.'</b>\');</script>';
|
||||
return $retval===0;
|
||||
}
|
||||
|
||||
@@ -492,7 +519,7 @@ function getAllocations() {
|
||||
}
|
||||
sort($port);
|
||||
$ip = $ct['NetworkMode']=='host'||$nat ? $host : ($ip ?: DockerUtil::myIP($ct['Name']) ?: '0.0.0.0');
|
||||
$list['Port'] = "$ip : ".(implode(' ',array_unique($port)) ?: '???')." -- {$ct['NetworkMode']}";
|
||||
$list['Port'] = "<span style='display:inline-block;width:90px'>{$ct['NetworkMode']}</span><span style='display:inline-block;width:140px'>$ip</span>".(implode(', ',array_unique($port)) ?: '???');
|
||||
$ports[] = $list;
|
||||
}
|
||||
return $ports;
|
||||
|
||||
@@ -17,46 +17,53 @@ require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
|
||||
$autostart_file = $dockerManPaths['autostart-file'];
|
||||
$template_repos = $dockerManPaths['template-repos'];
|
||||
$user_prefs = $dockerManPaths['user-prefs'];
|
||||
|
||||
// Update the start/stop configuration
|
||||
if ($_POST['action'] == 'autostart' ){
|
||||
$json = ($_POST['response'] == 'json') ? true : false;
|
||||
|
||||
if (!$json) readfile("$docroot/update.htm");
|
||||
$user_prefs = $dockerManPaths['user-prefs'];
|
||||
|
||||
switch ($_POST['action']) {
|
||||
case 'autostart':
|
||||
// update container autostart setting
|
||||
$container = urldecode(($_POST['container']));
|
||||
unset($_POST['container']);
|
||||
|
||||
$allAutoStart = @file($autostart_file, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$key = array_search($container, $allAutoStart);
|
||||
if ($key===false) {
|
||||
array_push($allAutoStart, $container);
|
||||
if ($json) echo json_encode(['autostart' => true]);
|
||||
$wait = $_POST['wait'];
|
||||
$item = rtrim("$container $wait");
|
||||
$autostart = @file($autostart_file, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$key = array_search($item, $autostart);
|
||||
if ($_POST['auto']=='true') {
|
||||
if ($key===false) $autostart[] = $item;
|
||||
} else {
|
||||
unset($allAutoStart[$key]);
|
||||
if ($json) echo json_encode(['autostart' => false]);
|
||||
unset($autostart[$key]);
|
||||
}
|
||||
// sort containers for start-up
|
||||
if (file_exists($user_prefs)) {
|
||||
$prefs = parse_ini_file($user_prefs); $sort = [];
|
||||
foreach ($allAutoStart as $ct) $sort[] = array_search($ct,$prefs) ?? 999;
|
||||
array_multisort($sort,SORT_NUMERIC,$allAutoStart);
|
||||
} else {
|
||||
natcasesort($allAutoStart);
|
||||
}
|
||||
$allAutoStart ? file_put_contents($autostart_file, implode(PHP_EOL, $allAutoStart).PHP_EOL) : @unlink($autostart_file);
|
||||
}
|
||||
|
||||
if ($_POST['#action'] == 'templates' ){
|
||||
if ($autostart) {
|
||||
if (file_exists($user_prefs)) {
|
||||
$prefs = parse_ini_file($user_prefs); $sort = [];
|
||||
foreach ($autostart as $ct) $sort[] = array_search(var_split($ct),$prefs) ?? 999;
|
||||
array_multisort($sort,$autostart);
|
||||
} else {
|
||||
natcasesort($autostart);
|
||||
}
|
||||
file_put_contents($autostart_file, implode("\n", $autostart)."\n");
|
||||
} else @unlink($autostart_file);
|
||||
break;
|
||||
case 'wait':
|
||||
// update wait period used after container autostart
|
||||
$container = urldecode(($_POST['container']));
|
||||
$wait = $_POST['wait'];
|
||||
$item = rtrim("$container $wait");
|
||||
$autostart = file($autostart_file, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$names = array_map('var_split', $autostart);
|
||||
$autostart[array_search($container,$names)] = $item;
|
||||
file_put_contents($autostart_file, implode("\n", $autostart)."\n");
|
||||
break;
|
||||
case 'templates':
|
||||
// update template
|
||||
readfile("$docroot/update.htm");
|
||||
$repos = $_POST['template_repos'];
|
||||
file_put_contents($template_repos, $repos);
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
$DockerTemplates->downloadTemplates();
|
||||
break;
|
||||
}
|
||||
|
||||
if ( isset($_GET['is_dir'] )) {
|
||||
if (isset($_GET['is_dir'])) {
|
||||
echo json_encode(['is_dir' => is_dir($_GET['is_dir'])]);
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -186,7 +186,7 @@ function resumeAll() {
|
||||
function checkAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
$('.updatecolumn').html('<span style="color:#267CA8"><i class="fa fa-refresh fa-spin"></i> checking...</span>');
|
||||
$.post('/plugins/dynamix.docker.manager/include/DockerUpdate.php',{check:true},function(u){loadlist(u);});
|
||||
loadlist();
|
||||
}
|
||||
function updateAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2017, Lime Technology
|
||||
* Copyright 2012-2017, Bergware International.
|
||||
/* Copyright 2005-2018, Lime Technology
|
||||
* Copyright 2012-2018, 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,
|
||||
@@ -20,9 +20,10 @@ function plugin($method, $arg = '') {
|
||||
return $retval==0 ? implode("\n", $output) : false;
|
||||
}
|
||||
|
||||
function check_plugin($arg, $dns='8.8.8.8') {
|
||||
// ping DNS server first to ensure internet is present
|
||||
return exec("ping -qnl2 -c2 -W3 $dns 2>/dev/null|awk '/received/{print $4}'") ? plugin('check',$arg) : false;
|
||||
function check_plugin($arg, &$ncsi) {
|
||||
// Get network connection status indicator (NCSI)
|
||||
if ($ncsi===null) passthru("wget --quiet --spider --timeout=10 --tries=1 http://www.msftncsi.com/ncsi.txt",$ncsi);
|
||||
return $ncsi===0 ? plugin('check',$arg) : false;
|
||||
}
|
||||
|
||||
function make_link($method, $arg, $extra='') {
|
||||
|
||||
@@ -23,6 +23,7 @@ $empty = true;
|
||||
$updates = 0;
|
||||
$builtin = ['unRAIDServer'];
|
||||
$plugins = "/var/log/plugins/*.plg";
|
||||
$ncsi = null; // network connection status indicator
|
||||
|
||||
if ($audit) {
|
||||
list($plg,$action) = explode(':',$audit);
|
||||
@@ -44,7 +45,7 @@ foreach (glob($plugins,GLOB_NOSORT) as $plugin_link) {
|
||||
//switch between system and custom plugins
|
||||
if (($system && !$custom) || (!$system && $custom)) continue;
|
||||
//forced plugin check?
|
||||
$checked = (!$audit && !$check) ? check_plugin(basename($plugin_file)) : true;
|
||||
$checked = (!$audit && !$check) ? check_plugin(basename($plugin_file),$ncsi) : true;
|
||||
//OS update?
|
||||
$os = $system && $name==$builtin[0];
|
||||
$toggle = false;
|
||||
@@ -56,7 +57,7 @@ foreach (glob($plugins,GLOB_NOSORT) as $plugin_link) {
|
||||
copy($plugin_file,$tmp_file);
|
||||
exec("sed -ri 's|^(<!ENTITY category).*|\\1 \"{$branch}\">|' $tmp_file");
|
||||
symlink($tmp_file,"/var/log/plugins/$tmp_plg");
|
||||
if (check_plugin($tmp_plg)) {
|
||||
if (check_plugin($tmp_plg,$ncsi)) {
|
||||
copy("/tmp/plugins/$tmp_plg",$tmp_file);
|
||||
$plugin_file = $tmp_file;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@ Markdown="false"
|
||||
?>
|
||||
<?
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
|
||||
exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list|sort -nu', $cpus);
|
||||
$cpus = cpu_list();
|
||||
|
||||
function showCPUs($uuid) {
|
||||
global $cpus;
|
||||
|
||||
@@ -346,7 +346,7 @@
|
||||
<td>
|
||||
<div class="textarea four">
|
||||
<?
|
||||
exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list|sort -nu', $cpus);
|
||||
$cpus = cpu_list();
|
||||
foreach ($cpus as $pair) {
|
||||
unset($cpu1,$cpu2);
|
||||
list($cpu1, $cpu2) = preg_split('/[,-]/',$pair);
|
||||
|
||||
@@ -632,7 +632,7 @@
|
||||
<td>
|
||||
<div class="textarea four">
|
||||
<?
|
||||
exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list|sort -nu', $cpus);
|
||||
$cpus = cpu_list();
|
||||
foreach ($cpus as $pair) {
|
||||
unset($cpu1,$cpu2);
|
||||
list($cpu1, $cpu2) = preg_split('/[,-]/',$pair);
|
||||
|
||||
@@ -632,7 +632,7 @@
|
||||
<td>
|
||||
<div class="textarea four">
|
||||
<?
|
||||
exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list|sort -nu', $cpus);
|
||||
$cpus = cpu_list();
|
||||
foreach ($cpus as $pair) {
|
||||
unset($cpu1,$cpu2);
|
||||
list($cpu1, $cpu2) = preg_split('/[,-]/',$pair);
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
Menu="OtherSettings"
|
||||
Title="CPU Pinning"
|
||||
Icon="grid.png"
|
||||
Tag="table"
|
||||
Cond="pgrep('libvirtd')!==false || pgrep('dockerd')!==false"
|
||||
---
|
||||
<?PHP
|
||||
/* Copyright 2005-2018, Lime Technology
|
||||
* Copyright 2012-2018, 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);
|
||||
|
||||
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++) {
|
||||
unset($cpu1,$cpu2);
|
||||
list($cpu1, $cpu2) = preg_split('/[,-]/',$cpus[$c*32+$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));
|
||||
}
|
||||
?>
|
||||
<style>
|
||||
table.tablesorter tr>th+th{text-align:right;vertical-align:top}
|
||||
table.tablesorter tr>td+td+td{vertical-align:top}
|
||||
table.tablesorter tr>th+th+th,table.tablesorter tr>td+td{text-align:center}
|
||||
table.tablesorter th:first-child,table.tablesorter td:first-child{width:180px;text-align:left}
|
||||
input[type=checkbox]{margin:0;padding:0}
|
||||
input.flat{margin:0}
|
||||
input.lock{margin:0}
|
||||
</style>
|
||||
<script>
|
||||
String.prototype.stripper = function(){return this.replace(/ |\(|\)|\[|\]/g,'');}
|
||||
|
||||
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;
|
||||
// get the 'checked' cpus
|
||||
$(form).find('input[type=checkbox]').each(function(){
|
||||
if ($(this).prop('checked')) args[$(this).prop('name')] = '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},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.stripper()).show('slow');
|
||||
// step 2: apply the changes by updating the vm or container
|
||||
$.post('/webGui/include/UpdateTwo.php',{id:id,name:encodeURI(name)},function(reply){
|
||||
if (reply.error) {
|
||||
// report error and reload table
|
||||
swal({type:'error', title:'Execution error', text:reply.error},function(){
|
||||
$('#wait-'+id).hide();
|
||||
$('input[value="Done"]').prop('disabled',false);
|
||||
reset($('form[name="'+id+'"]'));
|
||||
});
|
||||
} else {
|
||||
$('#'+id+'-'+reply.success.stripper()).hide('slow');
|
||||
// cleanup when all is done
|
||||
if (!--wait) {
|
||||
setTimeout(function(){$('#wait-'+id).hide();},500);
|
||||
$('input[value="Done"]').prop('disabled',false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$('#wait-'+id).hide();
|
||||
$('input[value="Done"]').prop('disabled',false);
|
||||
}
|
||||
});
|
||||
}
|
||||
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 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);
|
||||
});
|
||||
}
|
||||
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?>"); vm(); break;
|
||||
case 'ct': $('#table-ct').html("<?=$spinner?>"); ct(); break;
|
||||
}
|
||||
}
|
||||
function buttons(form) {
|
||||
// we need the Apply and Done buttons react on checkbox changes
|
||||
$(form).find('input[type=checkbox]').each(function(){$(this).on('input change',function(){
|
||||
$(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(){
|
||||
<?if ($libvirtd):?>
|
||||
vm();
|
||||
<?endif;?>
|
||||
<?if ($dockerd):?>
|
||||
ct();
|
||||
<?endif;?>
|
||||
});
|
||||
</script>
|
||||
|
||||
> This page gives a total view of the current CPU pinning assignments for both VMs and Docker containers.<br>
|
||||
> It also allows to modify these assignments.
|
||||
>
|
||||
> Running VMs or containers are **stopped first** and restarted after the modification.<br>
|
||||
> Stopped VMs or containers are instantly modified and new assignments become active when the user manually starts the VM or container.
|
||||
>
|
||||
> When ***Apply*** is pressed a scan is performed to find the changes, subsequently only VMs or containers which have changes are modified in parallel.
|
||||
>
|
||||
> *Please wait until all updates are finished*.
|
||||
|
||||
<?if ($libvirtd):?>
|
||||
<form name="vm">
|
||||
<input type="hidden" name="names" id="names-vm" value="">
|
||||
<table class='tablesorter' style='width:auto'>
|
||||
<thead><tr><th><i class="fa fa-list"></i> VM</th><?create()?></tr></thead>
|
||||
<tbody id="table-vm"><?=$spinner?></tbody>
|
||||
</table>
|
||||
<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> Please wait...</span>
|
||||
</form>
|
||||
<br>
|
||||
<?endif;?>
|
||||
<?if ($dockerd):?>
|
||||
<form name="ct">
|
||||
<input type="hidden" name="names" id="names-ct" value="">
|
||||
<table class='tablesorter' style='width:auto'>
|
||||
<thead><tr><th><i class="fa fa-list"></i> Container</th><?create()?></tr></thead>
|
||||
<tbody id="table-ct"><?=$spinner?></tbody>
|
||||
</table>
|
||||
<input type="button" value="Apply" onclick="apply(this.form)" disabled><input type="button" value="Done" onclick="done()"><span id="wait-ct" class="red-text" style="display:none"><i class="fa fa-spinner fa-spin"></i> Please wait...</span>
|
||||
</form>
|
||||
<?endif;?>
|
||||
@@ -235,7 +235,7 @@ $dck = exec("df /var/lib/docker|grep -om1 '^/'");
|
||||
<tr class='wide'><td>Memory usage</td><td colspan='3'><div class='usage-disk sys'><span id='sys0' style='width:0'></span></div></td></tr>
|
||||
<tr class='wide'><td>flash log docker</td><td><div class='usage-disk sys'><span id='sys1' style='width:0'></span></div></td><td><div class='usage-disk sys'><span id='sys2' style='width:0'></span></div></td>
|
||||
<td><?if ($dck):?><div class='usage-disk sys'><span id='sys3' style='width:0'></span></div><?else:?>Not available<?endif;?></td></tr>
|
||||
<tr><td rowspan='2'>Memory size</td><td>allocated</td><td colspan='2' class='blue'><?=my_scale($total,$unit,3)." $unit"?></td></tr>
|
||||
<tr><td rowspan='2'>Memory size</td><td>total usable</td><td colspan='2' class='blue'><?=my_scale($total,$unit,3)." $unit"?></td></tr>
|
||||
<tr><td>installed</td><td colspan='2' class='blue'><?="$memory_installed $size ".($ecc_support=='None'?'':$ecc_support)."<br>max. installable capacity $memory_maximum $size"?></td></tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,87 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2018, Lime Technology
|
||||
* Copyright 2012-2018, 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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
$cpus = explode(';',$_POST['cpus']);
|
||||
|
||||
function create($id, $name, $vcpu) {
|
||||
// create the list of checkboxes. Make multiple rows when CPU cores are many ;)
|
||||
global $cpus;
|
||||
$total = count($cpus);
|
||||
$loop = floor(($total-1)/32)+1;
|
||||
$text = [];
|
||||
$unit = str_replace([' ','(',')','[',']'],'',$name);
|
||||
$name = urlencode($name);
|
||||
echo "<td><span id='$id-$unit' style='color:#267CA8;display:none'><i class='fa fa-refresh fa-spin'></i> updating</span></td>";
|
||||
for ($c = 0; $c < $loop; $c++) {
|
||||
$max = ($c == $loop-1 ? ($total%32?:32) : 32);
|
||||
for ($n = 0; $n < $max; $n++) {
|
||||
unset($cpu1,$cpu2);
|
||||
list($cpu1, $cpu2) = preg_split('/[,-]/',$cpus[$c*32+$n]);
|
||||
$check1 = in_array($cpu1, $vcpu) ? 'checked':'';
|
||||
$check2 = $cpu2 ? (in_array($cpu2, $vcpu) ? 'checked':''):'';
|
||||
$text[$n] .="<input type='checkbox' name='$name:$cpu1' $check1><br>";
|
||||
if ($cpu2) $text[$n] .= "<input type='checkbox' name='$name:$cpu2' $check2><br>";
|
||||
}
|
||||
}
|
||||
echo implode(array_map(function($t){return "<td>$t</td>";},$text));
|
||||
}
|
||||
|
||||
switch ($_POST['id']) {
|
||||
case 'vm':
|
||||
// create the current vm assignments
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
$vms = $libvirt_running=='yes' ? $lv->get_domains() : [];
|
||||
$user_prefs = '/boot/config/plugins/dynamix.vm.manager/userprefs.cfg';
|
||||
// list vms per user preference
|
||||
if (file_exists($user_prefs)) {
|
||||
$prefs = parse_ini_file($user_prefs); $sort = [];
|
||||
foreach ($vms as $vm) $sort[] = array_search($vm,$prefs) ?? 999;
|
||||
array_multisort($sort,SORT_NUMERIC,$vms);
|
||||
} else {
|
||||
natcasesort($vms);
|
||||
}
|
||||
foreach ($vms as $vm) {
|
||||
$uuid = $lv->domain_get_uuid($lv->get_domain_by_name($vm));
|
||||
$cfg = domain_to_config($uuid);
|
||||
echo "<tr><td>$vm</td>";
|
||||
create('vm', $vm, $cfg['domain']['vcpu']);
|
||||
echo "</tr>";
|
||||
}
|
||||
// return the cpu assignments and available VM names
|
||||
echo "\0".implode(';',array_map('urlencode',$vms));
|
||||
break;
|
||||
case 'ct':
|
||||
// create the current container assignments
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
$DockerClient = new DockerClient();
|
||||
$containers = $DockerClient->getDockerContainers();
|
||||
$user_prefs = $dockerManPaths['user-prefs'];
|
||||
$cts = []; foreach ($containers as $ct) $cts[] = $ct['Name'];
|
||||
// list containers per user preference
|
||||
if (file_exists($user_prefs)) {
|
||||
$prefs = parse_ini_file($user_prefs); $sort = [];
|
||||
foreach ($containers as $ct) $sort[] = array_search($ct['Name'],$prefs) ?? 999;
|
||||
array_multisort($sort,SORT_NUMERIC,$containers);
|
||||
unset($sort);
|
||||
}
|
||||
foreach ($containers as $ct) {
|
||||
echo "<tr><td>{$ct['Name']}</td>";
|
||||
create('ct', $ct['Name'], explode(',',$ct['CPUset']));
|
||||
echo "</tr>";
|
||||
}
|
||||
// return the cpu assignments and available container names
|
||||
echo "\0".implode(';',array_map('urlencode',$cts));
|
||||
break;
|
||||
}
|
||||
?>
|
||||
@@ -499,8 +499,8 @@ $(function() {
|
||||
$('input[value="Apply"],input[name="cmdEditShare"],input[name="cmdUserEdit"]').attr('disabled','disabled');
|
||||
$('form').find('select,input[type=text],input[type=number],input[type=password],input[type=checkbox],input[type=file],textarea').each(function(){$(this).on('input change',function() {
|
||||
var form = $(this).parentsUntil('form').parent();
|
||||
form.find('input[value="Apply"],input[name="cmdEditShare"],input[name="cmdUserEdit"]').not('input.lock').removeAttr('disabled');
|
||||
form.find('input[value="Done"]').not('input.lock').val('Reset').prop('onclick',null).click(function(){refresh(form.offset().top)});
|
||||
form.find('input[value="Apply"],input[name="cmdEditShare"],input[name="cmdUserEdit"]').not('input.lock').prop('disabled',false);
|
||||
form.find('input[value="Done"]').not('input.lock').val('Reset').prop('onclick',null).off('click').click(function(){refresh(form.offset().top)});
|
||||
});});
|
||||
|
||||
var top = ($.cookie('top')||0) - $('.tabs').offset().top - 75;
|
||||
|
||||
@@ -262,4 +262,8 @@ function my_parse_ini_string($text, $sections=false, $scanner=INI_SCANNER_NORMAL
|
||||
function my_parse_ini_file($file, $sections=false, $scanner=INI_SCANNER_NORMAL) {
|
||||
return my_parse_ini_string(file_get_contents($file),$sections,$scanner);
|
||||
}
|
||||
function cpu_list() {
|
||||
exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list|sort -nu', $cpus);
|
||||
return $cpus;
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2018, Lime Technology
|
||||
* Copyright 2012-2018, 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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
$map = $changes = [];
|
||||
foreach (array_map('urldecode',explode(';',$_POST['names'])) as $name) $map[$name] = '';
|
||||
|
||||
foreach($_POST as $key => $val) {
|
||||
if ($val != 'on') continue;
|
||||
list($name,$cpu) = explode(':',$key);
|
||||
$map[urldecode($name)] .= "$cpu,";
|
||||
}
|
||||
// map holds the list of each vm or container 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;
|
||||
}
|
||||
// signal changes
|
||||
header('Content-Type: application/json');
|
||||
die(json_encode($reply));
|
||||
?>
|
||||
@@ -0,0 +1,120 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2018, Lime Technology
|
||||
* Copyright 2012-2018, 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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
$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;
|
||||
$vendor = exec("grep -Pom1 '^vendor_id\\s+: \\K\\S+' /proc/cpuinfo");
|
||||
if ($vendor == 'AuthenticAMD') {
|
||||
$ht = 1; // force single threaded for AMD
|
||||
} else {
|
||||
$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;
|
||||
}
|
||||
$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;
|
||||
unset($xml->cputune);
|
||||
$xml->addChild('cputune');
|
||||
for ($i = 0; $i < $vcpus; $i++) {
|
||||
$vcpu = $xml->cputune->addChild('vcpupin');
|
||||
$vcpu['vcpu'] = $i;
|
||||
$vcpu['cpuset'] = $cpuset[$i];
|
||||
}
|
||||
// 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";
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/Helpers.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,30);
|
||||
}
|
||||
// force kill container if still running after 30 seconds
|
||||
$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;
|
||||
}
|
||||
header('Content-Type: application/json');
|
||||
die(json_encode($reply));
|
||||
?>
|
||||
@@ -22,6 +22,11 @@ $var = file_exists("$get/var.ini") ? parse_ini_file("$get/var.ini") : [];
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
$folders = ['/boot','/boot/config','/boot/config/plugins','/boot/extra','/boot/syslinux','/var/log','/var/log/plugins','/var/log/packages','/tmp'];
|
||||
|
||||
function exert($cmd, &$save=null) {
|
||||
// execute command with timeout of 30s
|
||||
exec("timeout -s9 30 $cmd", $save);
|
||||
}
|
||||
|
||||
function anonymize($text,$select) {
|
||||
global $all;
|
||||
if ($all) return $text;
|
||||
@@ -51,7 +56,7 @@ function anonymize($text,$select) {
|
||||
if ($cli) {
|
||||
// script is called from CLI
|
||||
echo "Starting diagnostics collection... ";
|
||||
exec("mkdir -p /boot/logs");
|
||||
exert("mkdir -p /boot/logs");
|
||||
$server = isset($var['NAME']) ? str_replace(' ','_',strtolower($var['NAME'])) : 'tower';
|
||||
$date = date('Ymd-Hi');
|
||||
$diag = "$server-diagnostics-$date";
|
||||
@@ -63,9 +68,9 @@ if ($cli) {
|
||||
$date = "{$split[2]}-{$split[3]}";
|
||||
}
|
||||
// create folder structure
|
||||
exec("mkdir -p ".escapeshellarg("/$diag/system")." ".escapeshellarg("/$diag/config")." ".escapeshellarg("/$diag/logs")." ".escapeshellarg("/$diag/shares")." ".escapeshellarg("/$diag/smart")." ".escapeshellarg("/$diag/qemu"));
|
||||
exert("mkdir -p ".escapeshellarg("/$diag/system")." ".escapeshellarg("/$diag/config")." ".escapeshellarg("/$diag/logs")." ".escapeshellarg("/$diag/shares")." ".escapeshellarg("/$diag/smart")." ".escapeshellarg("/$diag/qemu"));
|
||||
// get utilization of running processes
|
||||
exec("top -bn1 -o%CPU 2>/dev/null|todos >".escapeshellarg("/$diag/system/top.txt"));
|
||||
exert("top -bn1 -o%CPU 2>/dev/null|todos >".escapeshellarg("/$diag/system/top.txt"));
|
||||
// make unRAID version reference
|
||||
$unraid = parse_ini_file('/etc/unraid-version');
|
||||
file_put_contents("/$diag/unRAID-".$unraid['version'].".txt",$unraid['version']);
|
||||
@@ -76,7 +81,7 @@ foreach (glob("$get/*.ini") as $file) {
|
||||
if ($all || $ini != "users") file_put_contents("/$diag/system/vars.txt",preg_replace(["/\n/","/^Array/"],["\r\n",$ini],anonymize(print_r(parse_ini_file($file,true),true),1)),FILE_APPEND);
|
||||
}
|
||||
// Create loads.txt
|
||||
$cpuload = exec("uptime")." Cores: ".exec("nproc")."\r\n".file_get_contents("/var/local/emhttp/cpuload.ini")."\r\n";
|
||||
$cpuload = exert("uptime")." Cores: ".exert("nproc")."\r\n".file_get_contents("/var/local/emhttp/cpuload.ini")."\r\n";
|
||||
$diskload = file("/var/local/emhttp/diskload.ini");
|
||||
$disks = parse_ini_file("/var/local/emhttp/disks.ini",true);
|
||||
foreach ( $diskload as $loadLine ) {
|
||||
@@ -90,38 +95,39 @@ foreach ( $diskload as $loadLine ) {
|
||||
}
|
||||
file_put_contents("/$diag/system/loads.txt",$cpuload.implode("\r\n",$loadTxt));
|
||||
// individual commands execution (suppress errors)
|
||||
exec("lscpu 2>/dev/null|todos >".escapeshellarg("/$diag/system/lscpu.txt"));
|
||||
exec("lsscsi -vgl 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsscsi.txt"));
|
||||
exec("lspci -knn 2>/dev/null|todos >".escapeshellarg("/$diag/system/lspci.txt"));
|
||||
exec("lsusb 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsusb.txt"));
|
||||
exec("free -mth 2>/dev/null|todos >".escapeshellarg("/$diag/system/memory.txt"));
|
||||
exec("ps -auxf --sort=-pcpu 2>/dev/null|todos >".escapeshellarg("/$diag/system/ps.txt"));
|
||||
exec("lsof -Pni 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsof.txt"));
|
||||
exec("lsmod|sort 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsmod.txt"));
|
||||
exec("df -h 2>/dev/null|todos >".escapeshellarg("/$diag/system/df.txt"));
|
||||
exec("ifconfig -a -s 2>/dev/null|grep -Po '^(eth|bond)[0-9]+'", $ports);
|
||||
exec("dmidecode -qt2|awk -F: '/^\tManufacturer:/{m=\$2};/^\tProduct Name:/{p=\$2} END{print m\" -\"p}' 2>/dev/null|todos >".escapeshellarg("/$diag/system/motherboard.txt"));
|
||||
exec("dmidecode -qt0 2>/dev/null|todos >>".escapeshellarg("/$diag/system/motherboard.txt"));
|
||||
exert("lscpu 2>/dev/null|todos >".escapeshellarg("/$diag/system/lscpu.txt"));
|
||||
exert("lsscsi -vgl 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsscsi.txt"));
|
||||
exert("lspci -knn 2>/dev/null|todos >".escapeshellarg("/$diag/system/lspci.txt"));
|
||||
exert("lsusb 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsusb.txt"));
|
||||
exert("free -mth 2>/dev/null|todos >".escapeshellarg("/$diag/system/memory.txt"));
|
||||
exert("ps -auxf --sort=-pcpu 2>/dev/null|todos >".escapeshellarg("/$diag/system/ps.txt"));
|
||||
exert("lsof -Pni 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsof.txt"));
|
||||
exert("lsmod|sort 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsmod.txt"));
|
||||
exert("df -h 2>/dev/null|todos >".escapeshellarg("/$diag/system/df.txt"));
|
||||
exert("ifconfig -a -s 2>/dev/null|grep -Po '^(eth|bond)[0-9]+'", $ports);
|
||||
exert("dmidecode -qt2|awk -F: '/^\tManufacturer:/{m=\$2};/^\tProduct Name:/{p=\$2} END{print m\" -\"p}' 2>/dev/null|todos >".escapeshellarg("/$diag/system/motherboard.txt"));
|
||||
exert("dmidecode -qt0 2>/dev/null|todos >>".escapeshellarg("/$diag/system/motherboard.txt"));
|
||||
exert("cat /proc/meminfo 2>/dev/null|todos >".escapeshellarg("/$diag/system/meminfo.txt"));
|
||||
// create ethernet information information (suppress errors)
|
||||
foreach ($ports as $port) {
|
||||
exec("ethtool ".escapeshellarg($port)." 2>/dev/null|todos >>".escapeshellarg("/$diag/system/ethtool.txt"));
|
||||
exert("ethtool ".escapeshellarg($port)." 2>/dev/null|todos >>".escapeshellarg("/$diag/system/ethtool.txt"));
|
||||
file_put_contents("/$diag/system/ethtool.txt", "\r\n", FILE_APPEND);
|
||||
exec("ethtool -i ".escapeshellarg($port)." 2>/dev/null|todos >>".escapeshellarg("/$diag/system/ethtool.txt"));
|
||||
exert("ethtool -i ".escapeshellarg($port)." 2>/dev/null|todos >>".escapeshellarg("/$diag/system/ethtool.txt"));
|
||||
file_put_contents("/$diag/system/ethtool.txt", "--------------------------------\r\n", FILE_APPEND);
|
||||
}
|
||||
exec("ifconfig -a 2>/dev/null|todos >".escapeshellarg("/$diag/system/ifconfig.txt"));
|
||||
exert("ifconfig -a 2>/dev/null|todos >".escapeshellarg("/$diag/system/ifconfig.txt"));
|
||||
// create system information (suppress errors)
|
||||
exec("find /sys/kernel/iommu_groups/ -type l 2>/dev/null|todos >".escapeshellarg("/$diag/system/iommu_groups.txt"));
|
||||
exec("todos </proc/cmdline >".escapeshellarg("/$diag/system/cmdline.txt"));
|
||||
exert("find /sys/kernel/iommu_groups/ -type l 2>/dev/null|sort -V|todos >".escapeshellarg("/$diag/system/iommu_groups.txt"));
|
||||
exert("todos </proc/cmdline >".escapeshellarg("/$diag/system/cmdline.txt"));
|
||||
// create folder structure listing
|
||||
$dest = "/$diag/system/folders.txt";
|
||||
foreach ($folders as $folder) {
|
||||
if (is_dir($folder)) exec("echo -ne ".escapeshellarg("\r\n$folder\r\n")." >>".escapeshellarg($dest).";ls -l ".escapeshellarg($folder)."|todos >>".escapeshellarg("$dest")); else exec("echo -ne ".escapeshellarg("\r\n$folder\r\nfolder does not exist\r\n")." >>".escapeshellarg("$dest"));
|
||||
if (is_dir($folder)) exert("echo -ne ".escapeshellarg("\r\n$folder\r\n")." >>".escapeshellarg($dest).";ls -l ".escapeshellarg($folder)."|todos >>".escapeshellarg("$dest")); else exert("echo -ne ".escapeshellarg("\r\n$folder\r\nfolder does not exist\r\n")." >>".escapeshellarg("$dest"));
|
||||
}
|
||||
// copy configuration files (suppress errors)
|
||||
exec("cp /boot/config/*.{cfg,conf,dat} /boot/config/go ".escapeshellarg("/$diag/config")." 2>/dev/null");
|
||||
exert("cp /boot/config/*.{cfg,conf,dat} /boot/config/go ".escapeshellarg("/$diag/config")." 2>/dev/null");
|
||||
// anonymize configuration files
|
||||
if (!$all) exec("sed -ri 's/^((disk|flash)(Read|Write)List.*=\")[^\"]+/\\1.../' ".escapeshellarg("/$diag/config/*.cfg")." 2>/dev/null");
|
||||
if (!$all) exert("sed -ri 's/^((disk|flash)(Read|Write)List.*=\")[^\"]+/\\1.../' ".escapeshellarg("/$diag/config/*.cfg")." 2>/dev/null");
|
||||
|
||||
// don't anonymize system share names
|
||||
$vardomain = file_exists('/boot/config/domain.cfg') ? parse_ini_file('/boot/config/domain.cfg') : [];
|
||||
@@ -143,7 +149,7 @@ foreach ($files as $file) {
|
||||
$dest = anonymize($dest,2);
|
||||
}
|
||||
@copy($file, $dest);
|
||||
if (!$all) exec("sed -ri 's/^(share(Comment|ReadList|WriteList)=\")[^\"]+/\\1.../' ".escapeshellarg($dest)." 2>/dev/null");
|
||||
if (!$all) exert("sed -ri 's/^(share(Comment|ReadList|WriteList)=\")[^\"]+/\\1.../' ".escapeshellarg($dest)." 2>/dev/null");
|
||||
}
|
||||
// create default user shares information
|
||||
$shares = file_exists("$get/shares.ini") ? parse_ini_file("$get/shares.ini", true) : [];
|
||||
@@ -156,17 +162,17 @@ $max = 1*1024*1024; //=1MB
|
||||
$docker = "/var/log/docker.log";
|
||||
if (file_exists($docker)) {
|
||||
$log = "/$diag/logs/docker";
|
||||
exec("todos <$docker >".escapeshellarg("$log.txt"));
|
||||
exert("todos <$docker >".escapeshellarg("$log.txt"));
|
||||
if (filesize($docker)>=$max) {
|
||||
exec("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
exec("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
exert("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
exert("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
}
|
||||
}
|
||||
// create SMART reports (suppress errors)
|
||||
$disks = file_exists("$get/disks.ini") ? parse_ini_file("$get/disks.ini", true) : [];
|
||||
include_once "$docroot/webGui/include/CustomMerge.php";
|
||||
include_once "$docroot/webGui/include/Wrappers.php";
|
||||
exec("ls -l /dev/disk/by-id/[asun]* 2>/dev/null|sed '/-part/d;s|^.*/by-id/[^-]*-||;s|-> ../../||;s|:|-|'", $devices);
|
||||
exert("ls -l /dev/disk/by-id/[asun]* 2>/dev/null|sed '/-part/d;s|^.*/by-id/[^-]*-||;s|-> ../../||;s|:|-|'", $devices);
|
||||
foreach ($devices as $device) {
|
||||
list($name,$port) = explode(' ',$device);
|
||||
$diskName = ''; $type = '';
|
||||
@@ -181,16 +187,16 @@ foreach ($devices as $device) {
|
||||
}
|
||||
$port = port_name($port);
|
||||
$status = $find['status'] == "DISK_OK" ? "" : " - {$find['status']}";
|
||||
exec("smartctl -x $type ".escapeshellarg("/dev/$port")." 2>/dev/null|todos >".escapeshellarg("/$diag/smart/$name-$date $diskName ($port)$status.txt"));
|
||||
exert("smartctl -x $type ".escapeshellarg("/dev/$port")." 2>/dev/null|todos >".escapeshellarg("/$diag/smart/$name-$date $diskName ($port)$status.txt"));
|
||||
}
|
||||
// create cache pool information
|
||||
if (is_dir('/mnt/cache') && strpos($disks['cache']['fsType'],'btrfs')!==false) {
|
||||
exec("/sbin/btrfs filesystem usage /mnt/cache 2>/dev/null|todos >".escapeshellarg("/$diag/system/btrfs-usage.txt"));
|
||||
exert("/sbin/btrfs filesystem usage /mnt/cache 2>/dev/null|todos >".escapeshellarg("/$diag/system/btrfs-usage.txt"));
|
||||
}
|
||||
// create installed plugin information
|
||||
$plugins = glob("/var/log/plugins/*.plg");
|
||||
foreach ($plugins as $plugin) {
|
||||
$installedPlugins .= basename($plugin)." - ".exec("plugin version ".escapeshellarg($plugin))."\r\n";
|
||||
$installedPlugins .= basename($plugin)." - ".exert("plugin version ".escapeshellarg($plugin))."\r\n";
|
||||
}
|
||||
$installedPlugins = $installedPlugins ?: "No additional Plugins Installed";
|
||||
file_put_contents("/$diag/system/plugins.txt",$installedPlugins);
|
||||
@@ -198,10 +204,10 @@ file_put_contents("/$diag/system/plugins.txt",$installedPlugins);
|
||||
$libvirtd = "/var/log/libvirt/libvirtd.log";
|
||||
if (file_exists($libvirtd)) {
|
||||
$log = "/$diag/logs/libvirt";
|
||||
exec("todos <$libvirtd >".escapeshellarg("$log.txt"));
|
||||
exert("todos <$libvirtd >".escapeshellarg("$log.txt"));
|
||||
if (filesize($libvirtd)>=$max) {
|
||||
exec("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
exec("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
exert("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
exert("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
}
|
||||
}
|
||||
// copy VMs information (if existing)
|
||||
@@ -209,10 +215,10 @@ $qemu = glob("/var/log/libvirt/qemu/*.log*");
|
||||
if ($qemu) {
|
||||
foreach ($qemu as $file) {
|
||||
$log = "/$diag/qemu/".basename($file,'.log');
|
||||
exec("todos <".escapeshellarg($file)." >".escapeshellarg("$log.txt"));
|
||||
exert("todos <".escapeshellarg($file)." >".escapeshellarg("$log.txt"));
|
||||
if (filesize($file)>=$max) {
|
||||
exec("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
exec("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
exert("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
exert("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
}
|
||||
}
|
||||
} else
|
||||
@@ -221,26 +227,26 @@ if ($qemu) {
|
||||
$max = 2*1024*1024; //=2MB
|
||||
foreach (glob("/var/log/syslog*") as $file) {
|
||||
$log = "/$diag/logs/".basename($file);
|
||||
exec("todos <".escapeshellarg($file)." >".escapeshellarg("$log.txt"));
|
||||
exert("todos <".escapeshellarg($file)." >".escapeshellarg("$log.txt"));
|
||||
if (!$all) {
|
||||
unset($titles,$rows);
|
||||
exec("grep -Po 'logger: moving \"\K[^\"]+' ".escapeshellarg("$log.txt")." 2>/dev/null|sort|uniq", $titles);
|
||||
exec("sed -ri 's|\b\S+@\S+\.\S+\b|xxx@removed.com|;s|\b(username\|password)([=:])\S+\b|\\1\\2xxx|;s|(GUID: \S)\S+(\S) |\\1..\\2 |;s|(moving \"\S\|\"/mnt/user/\S).*(\S)\"|\\1..\\2\"|' ".escapeshellarg("$log.txt"));
|
||||
exec("sed -ri 's|(host: \").+(\.unraid\.net:[0-9]+\")|\\1hash\\2|;s|(referrer: \"https?://).+(\.unraid\.net)|\\1hash\\2|' ".escapeshellarg("$log.txt"));
|
||||
exert("grep -Po 'logger: moving \"\K[^\"]+' ".escapeshellarg("$log.txt")." 2>/dev/null|sort|uniq", $titles);
|
||||
exert("sed -ri 's|\b\S+@\S+\.\S+\b|xxx@removed.com|;s|\b(username\|password)([=:])\S+\b|\\1\\2xxx|;s|(GUID: \S)\S+(\S) |\\1..\\2 |;s|(moving \"\S\|\"/mnt/user/\S).*(\S)\"|\\1..\\2\"|' ".escapeshellarg("$log.txt"));
|
||||
exert("sed -ri 's|(host: \").+(\.unraid\.net:[0-9]+\")|\\1hash\\2|;s|(referrer: \"https?://).+(\.unraid\.net)|\\1hash\\2|' ".escapeshellarg("$log.txt"));
|
||||
foreach ($titles as $mover) {
|
||||
$title = "/{$mover[0]}..".substr($mover,-1)."/...";
|
||||
exec("sed -ri 's|(logger: [.>cr].*)[ /]$mover/.*$|\\1 file: $title|' ".escapeshellarg("$log.txt")." 2>/dev/null");
|
||||
exert("sed -ri 's|(logger: [.>cr].*)[ /]$mover/.*$|\\1 file: $title|' ".escapeshellarg("$log.txt")." 2>/dev/null");
|
||||
}
|
||||
exec("grep -n ' cache_dirs: -' ".escapeshellarg("$log.txt")." 2>/dev/null|cut -d: -f1", $rows);
|
||||
for ($i = 0; $i < count($rows); $i += 2) for ($row = $rows[$i]+1; $row < $rows[$i+1]; $row++) exec("sed -ri '$row s|(cache_dirs: \S).*(\S)|\\1..\\2|' ".escapeshellarg("$log.txt")." 2>/dev/null");
|
||||
exert("grep -n ' cache_dirs: -' ".escapeshellarg("$log.txt")." 2>/dev/null|cut -d: -f1", $rows);
|
||||
for ($i = 0; $i < count($rows); $i += 2) for ($row = $rows[$i]+1; $row < $rows[$i+1]; $row++) exert("sed -ri '$row s|(cache_dirs: \S).*(\S)|\\1..\\2|' ".escapeshellarg("$log.txt")." 2>/dev/null");
|
||||
}
|
||||
// replace consecutive repeated lines in syslog
|
||||
exec("awk -i inplace '{if(s!=substr(\$0,17)){if(x>0)print\"### [PREVIOUS LINE REPEATED \"x\" TIMES] ###\\r\";print;x=0}else{x++}s=substr(\$0,17)}END{if(x>0)print\"### [PREVIOUS LINE REPEATED \"x\" TIMES] ###\\r\"}' ".escapeshellarg("$log.txt"));
|
||||
exert("awk -i inplace '{if(s!=substr(\$0,17)){if(x>0)print\"### [PREVIOUS LINE REPEATED \"x\" TIMES] ###\\r\";print;x=0}else{x++}s=substr(\$0,17)}END{if(x>0)print\"### [PREVIOUS LINE REPEATED \"x\" TIMES] ###\\r\"}' ".escapeshellarg("$log.txt"));
|
||||
// truncate syslog if too big
|
||||
if (basename($file)=='syslog' && filesize($file)>=$max) exec("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
exec("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
if (basename($file)=='syslog' && filesize($file)>=$max) exert("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
exert("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
}
|
||||
// create resulting zip file and remove temp folder
|
||||
exec("zip -qmr ".escapeshellarg($zip)." ".escapeshellarg("/$diag"));
|
||||
exert("zip -qmr ".escapeshellarg($zip)." ".escapeshellarg("/$diag"));
|
||||
if ($cli) echo "done.\nZIP file '$zip' created.\n";
|
||||
?>
|
||||
|
||||
@@ -63,8 +63,8 @@ form+p{display:none}
|
||||
#nav-item a{color:#A6A7A7;text-decoration:none;padding:19px 80px 12px 16px}
|
||||
#nav-item a:before{font-family:docker-icon,fontawesome;font-size:26px;margin-right:25px}
|
||||
#nav-item.active,#nav-item.active a{color:#5D6833;background:#ABC056}
|
||||
#nav-item.HelpButton.active:hover{background:#ABC056;font-size:18px}
|
||||
#nav-item.HelpButton.active{background:#121510;font-size:0}
|
||||
#nav-item.HelpButton.active:hover,#nav-item.HelpButton.active a:hover{background:#ABC056;font-size:18px}
|
||||
#nav-item.HelpButton.active,#nav-item.HelpButton.active a{background:#121510;font-size:0}
|
||||
#nav-item a i{display:none}
|
||||
#nav-user{display:block;float:left;text-align:center;border:none}
|
||||
#nav-user.probe{font-size:11px;margin-left:3px;margin-right:8px}
|
||||
|
||||
@@ -63,8 +63,8 @@ form+p{display:none}
|
||||
#nav-item a{color:#A6A7A7;text-decoration:none;padding:19px 80px 12px 16px}
|
||||
#nav-item a:before{font-family:docker-icon,fontawesome;font-size:26px;margin-right:25px}
|
||||
#nav-item.active,#nav-item.active a{color:#5D6833;background:#ABC056}
|
||||
#nav-item.HelpButton.active:hover{background:#ABC056;font-size:18px}
|
||||
#nav-item.HelpButton.active{background:#121510;font-size:0}
|
||||
#nav-item.HelpButton.active:hover,#nav-item.HelpButton.active a:hover{background:#ABC056;font-size:18px}
|
||||
#nav-item.HelpButton.active,#nav-item.HelpButton.active a{background:#121510;font-size:0}
|
||||
#nav-item a i{display:none}
|
||||
#nav-user{display:block;float:left;text-align:center;border:none}
|
||||
#nav-user.probe{font-size:11px;margin-left:3px;margin-right:8px}
|
||||
|
||||
Reference in New Issue
Block a user