summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2022-05-17 16:34:06 +0200
committerSimon Rettberg2022-05-17 16:34:06 +0200
commitb573ff04e6aefcb9050da330408e757410ede53b (patch)
tree1b8ad093b920cd2a261c2f1bb83538b8afba6921
parent[statistics] Make sure hwname and devpath get valid ASCII only (diff)
downloadslx-admin-b573ff04e6aefcb9050da330408e757410ede53b.tar.gz
slx-admin-b573ff04e6aefcb9050da330408e757410ede53b.tar.xz
slx-admin-b573ff04e6aefcb9050da330408e757410ede53b.zip
[dnbd3] Show upload speed and client count in realtime
-rw-r--r--modules-available/dnbd3/inc/dnbd3rpc.inc.php73
-rw-r--r--modules-available/dnbd3/lang/de/template-tags.json1
-rw-r--r--modules-available/dnbd3/lang/en/template-tags.json1
-rw-r--r--modules-available/dnbd3/page.inc.php37
-rw-r--r--modules-available/dnbd3/templates/page-serverlist.html63
5 files changed, 152 insertions, 23 deletions
diff --git a/modules-available/dnbd3/inc/dnbd3rpc.inc.php b/modules-available/dnbd3/inc/dnbd3rpc.inc.php
index 1a1e9f5c..9d7ba46c 100644
--- a/modules-available/dnbd3/inc/dnbd3rpc.inc.php
+++ b/modules-available/dnbd3/inc/dnbd3rpc.inc.php
@@ -82,4 +82,77 @@ class Dnbd3Rpc {
return $str;
}
+ /**
+ * Get statistics for multiple servers at once.
+ * @param string[] $servers
+ * @return array
+ */
+ public static function getStatsMulti(array $servers, int $timeout = 2): array
+ {
+ if (empty($servers))
+ return [];
+ $active = [];
+ $mh = curl_multi_init();
+ curl_multi_setopt($mh, CURLMOPT_MAXCONNECTS, 8);
+ curl_multi_setopt($mh, CURLMOPT_MAX_HOST_CONNECTIONS, 2);
+ curl_multi_setopt($mh, CURLMOPT_MAX_TOTAL_CONNECTIONS, 8);
+ foreach ($servers as $server) {
+ $url = 'http://' . self::translateServer($server) . '/query?q=stats';
+ $res = curl_init($url);
+ if ($res === false) {
+ error_log("curl_init($url) failed with $res");
+ continue;
+ }
+ curl_setopt_array($res, [
+ CURLOPT_CONNECTTIMEOUT => $timeout,
+ CURLOPT_TIMEOUT => $timeout,
+ CURLOPT_FOLLOWLOCATION => 0,
+ CURLOPT_ACCEPT_ENCODING => '', // Use everything libcurl supports
+ CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
+ CURLOPT_RETURNTRANSFER => 1,
+ ]);
+ $err = curl_multi_add_handle($mh, $res);
+ if ($err !== 0) {
+ error_log("curl_multi_add_handle() failed with $err");
+ curl_close($res);
+ } else {
+ $active[(int)$res] = $server;
+ }
+ }
+ // Wait
+ $running = 1;
+ $result = [];
+ $startTime = microtime(true);
+ for (;;) {
+ $ret = curl_multi_exec($mh, $running);
+ while (($info = curl_multi_info_read($mh)) !== false) {
+ if ($info['msg'] === CURLMSG_DONE) {
+ if (isset($active[(int)$info['handle']])) {
+ $server = $active[(int)$info['handle']];
+ unset($active[(int)$info['handle']]);
+ if ($info['result'] === CURLE_OK) {
+ $data = json_decode(curl_multi_getcontent($info['handle']), true);
+ if (is_array($data)) {
+ $data['ts'] = microtime(true);
+ $result[$server] = $data;
+ }
+ }
+ }
+ curl_multi_remove_handle($mh, $info['handle']);
+ curl_close($info['handle']);
+ }
+ }
+ $delay = ($startTime + $timeout) - microtime(true);
+ if ($ret !== CURLM_OK || !$running || $delay <= 0)
+ break;
+ $sret = curl_multi_select($mh, $delay);
+ if ($sret < 0) {
+ error_log("curl_multi_select returned $sret");
+ break;
+ }
+ }
+ curl_multi_close($mh);
+ return $result;
+ }
+
}
diff --git a/modules-available/dnbd3/lang/de/template-tags.json b/modules-available/dnbd3/lang/de/template-tags.json
index dfbd9317..cda5f83b 100644
--- a/modules-available/dnbd3/lang/de/template-tags.json
+++ b/modules-available/dnbd3/lang/de/template-tags.json
@@ -68,6 +68,7 @@
"lang_txTotal": "Gesamt gesendet",
"lang_unusedFor": "Ungenutzt",
"lang_uplink": "Uplink",
+ "lang_uploadSpeed": "Ausgehend",
"lang_uptime": "Aktuelle Laufzeit",
"lang_wantToDelete": "Wollen Sie diesen Server wirklich entfernen? (Rebooten\/Ausschalten muss in diesem Fall manuell vorgenommen werden)"
} \ No newline at end of file
diff --git a/modules-available/dnbd3/lang/en/template-tags.json b/modules-available/dnbd3/lang/en/template-tags.json
index 6cb81bb2..f1ee255c 100644
--- a/modules-available/dnbd3/lang/en/template-tags.json
+++ b/modules-available/dnbd3/lang/en/template-tags.json
@@ -68,6 +68,7 @@
"lang_txTotal": "Total sent",
"lang_unusedFor": "Unused",
"lang_uplink": "Uplink",
+ "lang_uploadSpeed": "Egress",
"lang_uptime": "Uptime",
"lang_wantToDelete": "Do you really want to delete this server? (Reboot\/Shutdown has to be done manually)"
} \ No newline at end of file
diff --git a/modules-available/dnbd3/page.inc.php b/modules-available/dnbd3/page.inc.php
index a58f6fcc..e90eff0f 100644
--- a/modules-available/dnbd3/page.inc.php
+++ b/modules-available/dnbd3/page.inc.php
@@ -492,6 +492,8 @@ class Page_Dnbd3 extends Page
$this->ajaxReboot();
} elseif ($action === 'cachemap') {
$this->ajaxCacheMap();
+ } elseif ($action === 'stats') {
+ $this->ajaxStats();
} else {
die($action . '???');
}
@@ -616,30 +618,23 @@ class Page_Dnbd3 extends Page
die($data);
}
- private function genChunk($acc)
+ private function ajaxStats()
{
- static $last = -1;
- static $count = 0;
- if ($acc !== false) {
- if ($acc > 15) {
- $acc = 15;
- }
- $acc = round($acc);
- if ($last === $acc) {
- $count++;
- return '';
- }
+ $res = Database::simpleQuery('SELECT s.serverid, m.clientip, s.fixedip
+ FROM dnbd3_server s
+ LEFT JOIN machine m ON (s.machineuuid = m.machineuuid)
+ WHERE s.lastseen > :cutoff', ['cutoff' => time() - 310]);
+ $lookup = [];
+ foreach ($res as $row) {
+ $lookup[$row['fixedip'] ?? $row['clientip'] ?? ''] = $row['serverid'];
}
- if ($last !== -1) {
- if ($count === 1)
- return '<b style="background:#0' . dechex($acc) . '0"></b>';
- $line = '<b style="background:#0' . dechex($last) . '0;flex-grow:' . $count . '"></b>';
- } else {
- $line = '';
+ $result = Dnbd3Rpc::getStatsMulti(array_keys($lookup));
+ $return = [];
+ foreach ($result as $ip => $data) {
+ $return[$lookup[$ip]] = $data;
}
- $last = $acc;
- $count = 1;
- return $line;
+ Header('Content-Type: application/json; charset=utf-8');
+ die(json_encode($return));
}
}
diff --git a/modules-available/dnbd3/templates/page-serverlist.html b/modules-available/dnbd3/templates/page-serverlist.html
index cdbd0789..d607e3a5 100644
--- a/modules-available/dnbd3/templates/page-serverlist.html
+++ b/modules-available/dnbd3/templates/page-serverlist.html
@@ -1,6 +1,10 @@
<h1>{{lang_dnbd3Management}}</h1>
<p><i>{{lang_dnbd3IntroText}}</i></p>
+<style>
+ .shd { text-shadow: #fff 1px 1px 2px }
+</style>
+
<div class="panel panel-default">
<div class="panel-heading">
{{lang_dnbd3Status}}:
@@ -53,6 +57,7 @@
<th>{{lang_proxyServerTHead}}</th>
<th class="text-right">{{lang_storageSize}}</th>
<th class="text-right">{{lang_clientCount}}</th>
+ <th>{{lang_uploadSpeed}}</th>
<th class="text-right">{{lang_lastSeen}}</th>
<th class="text-right">{{lang_uptime}}</th>
<th class="text-right">{{lang_txTotal}}</th>
@@ -97,7 +102,7 @@
</td>
<td data-sort="int" data-sort-default="desc" data-sort-value="{{disktotal}}">
<div style="border:1px solid #ddd;background:linear-gradient(to right, #f85 {{diskUsePercent}}%, transparent {{diskUsePercent}}%)"
- class="text-center text-nowrap"
+ class="text-center text-nowrap shd"
title="{{lang_diskFree}}: {{diskfree_s}}">
{{disktotal_s}}
</div>
@@ -107,9 +112,12 @@
</div>
{{/errormsg}}
</td>
- <td data-sort="int" data-sort-default="desc" class="text-right">
+ <td data-sort="int" data-sort-default="desc" class="text-right text-nowrap" id="clientcount-{{serverid}}">
{{clientcount}}
</td>
+ <td data-sort="int" data-sort-default="desc" class="text-right">
+ <div id="upspeed-{{serverid}}" style="min-width:100px;border:1px solid #ddd" class="text-center text-nowrap shd"></div>
+ </td>
<td data-sort="int" data-sort-default="desc" data-sort-value="{{dnbd3lastseen}}" class="text-right text-nowrap">
{{dnbd3lastseen_s}}
</td>
@@ -421,6 +429,57 @@ document.addEventListener('DOMContentLoaded', function () {
query();
rebootServerId = 0;
});
+ // live speed
+ var hiddenProp;
+ if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support
+ hiddenProp = "hidden";
+ } else if (typeof document.msHidden !== "undefined") {
+ hiddenProp = "msHidden";
+ } else if (typeof document.webkitHidden !== "undefined") {
+ hiddenProp = "webkitHidden";
+ } else {
+ hiddenProp = null;
+ }
+ var formatBytes = function(bytes) {
+ if (bytes < 1024) return bytes.toFixed(2) + ' B';
+ if (bytes < 1048576) return (bytes / 1024).toFixed(2) + ' KiB';
+ if (bytes < 1073741824) return (bytes / 1048576).toFixed(2) + ' MiB';
+ if (bytes < 1099511627776) return (bytes / 1073741824).toFixed(2) + ' GiB';
+ return (bytes / 1099511627776).toFixed(2) + ' TiB';
+ };
+ var calcBackgroundStyle = function(speed) {
+ const colors = ['#eee', '#cfc', '#6f6', '#90bf30', '#f00', '#f88'];
+ const limits = [1048576, 10485760, 104857600, 1073741824, 10737418240];
+ for (var i = 0; i < 4; ++i) {
+ if (speed < limits[i]) break;
+ }
+ const percent = Math.round(Math.max(0, Math.min(100, speed / limits[i] * 100)));
+ return { background: 'linear-gradient(90deg, ' + colors[i+1] + ' ' + percent + '%, ' + colors[i] + ' ' + percent + '%)' };
+ };
+ var lastSpeedList = {};
+ var updateSpeed = function() {
+ if (hiddenProp && document[hiddenProp])
+ return;
+ $.ajax('?do=dnbd3&action=stats').done(function(elist) {
+ for (var k in elist) {
+ var e = elist[k];
+ if (lastSpeedList[k]) {
+ var lastSpeed = lastSpeedList[k];
+ if (lastSpeed['ts'] < e['ts']) {
+ var $speed = $('#upspeed-' + k);
+ var s = (e['bytesSent'] - lastSpeed['bytesSent']) / (e['ts'] - lastSpeed['ts']);
+ $speed.text(formatBytes(s) + "/s").css(calcBackgroundStyle(s));
+ }
+ }
+ var $clients = $('#clientcount-' + k);
+ $clients.text(e['clientCount'] + ' + ' + e['serverCount']);
+ $clients.data('sort-value', e['clientCount'] + e['serverCount']);
+ }
+ lastSpeedList = elist;
+ });
+ };
+ updateSpeed();
+ setInterval(updateSpeed, 2500);
});
//--></script> \ No newline at end of file