From b573ff04e6aefcb9050da330408e757410ede53b Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 17 May 2022 16:34:06 +0200 Subject: [dnbd3] Show upload speed and client count in realtime --- modules-available/dnbd3/inc/dnbd3rpc.inc.php | 73 ++++++++++++++++++++++ modules-available/dnbd3/lang/de/template-tags.json | 1 + modules-available/dnbd3/lang/en/template-tags.json | 1 + modules-available/dnbd3/page.inc.php | 37 +++++------ .../dnbd3/templates/page-serverlist.html | 63 ++++++++++++++++++- 5 files changed, 152 insertions(+), 23 deletions(-) (limited to 'modules-available/dnbd3') 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 ''; - $line = ''; - } 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 @@

{{lang_dnbd3Management}}

{{lang_dnbd3IntroText}}

+ +
{{lang_dnbd3Status}}: @@ -53,6 +57,7 @@ {{lang_proxyServerTHead}} {{lang_storageSize}} {{lang_clientCount}} + {{lang_uploadSpeed}} {{lang_lastSeen}} {{lang_uptime}} {{lang_txTotal}} @@ -97,7 +102,7 @@
{{disktotal_s}}
@@ -107,9 +112,12 @@
{{/errormsg}} - + {{clientcount}} + +
+ {{dnbd3lastseen_s}} @@ -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); }); //--> \ No newline at end of file -- cgit v1.2.3-55-g7522