Merge pull request #359 from bergware/master

Use timeout in command execution of diagnostics
This commit is contained in:
tom mortensen
2018-08-11 14:39:16 -07:00
committed by GitHub
25 changed files with 764 additions and 200 deletions
@@ -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;
}
+1 -2
View 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);
+186
View File
@@ -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;?>
+1 -1
View File
@@ -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

+87
View File
@@ -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;
+4
View File
@@ -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;
}
?>
+79
View File
@@ -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));
?>
+120
View File
@@ -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));
?>
+55 -49
View File
@@ -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";
?>
+2 -2
View File
@@ -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}
+2 -2
View File
@@ -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}