From c124192e63bd8a48ed1e7caf9542dc52505dfd22 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 24 Jan 2019 12:17:07 +0100 Subject: [statistics] Handling of standby state in statistics, log crashes Or rather, not really crashes, but log whenever a client reports a poweron event without reporting a proper shutdown first. This isn't neccessarily a crash but could also be due to power loss, hard poweroff, or network failures. --- modules-available/statistics/page.inc.php | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'modules-available/statistics/page.inc.php') diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php index abacc8d2..2d86615e 100644 --- a/modules-available/statistics/page.inc.php +++ b/modules-available/statistics/page.inc.php @@ -910,16 +910,18 @@ class Page_Statistics extends Page //if ($cutoff < $client['firstseen']) $cutoff = $client['firstseen']; $scale = 100 / ($NOW - $cutoff); $res = Database::simpleQuery('SELECT dateline, typeid, data FROM statistic' - . " WHERE dateline > :cutoff AND typeid IN ('~session-length', '~offline-length') AND machineuuid = :uuid ORDER BY dateline ASC", array( + . " WHERE dateline > :cutoff AND typeid IN (:sessionLength, :offlineLength) AND machineuuid = :uuid ORDER BY dateline ASC", array( 'cutoff' => $cutoff - 86400 * 14, 'uuid' => $uuid, + 'sessionLength' => Statistics::SESSION_LENGTH, + 'offlineLength' => Statistics::OFFLINE_LENGTH, )); $spans['rows'] = array(); $spans['graph'] = ''; $last = false; $first = true; while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - if (!$client['isclient'] && $row['typeid'] === '~session-length') + if (!$client['isclient'] && $row['typeid'] === Statistics::SESSION_LENGTH) continue; // Don't differentiate between session and idle for non-clients if ($first && $row['dateline'] > $cutoff && $client['lastboot'] > $cutoff) { // Special case: offline before @@ -945,9 +947,12 @@ class Page_Statistics extends Page } $row['from'] = Util::prettyTime($row['dateline']); $row['duration'] = floor($row['data'] / 86400) . 'd ' . gmdate('H:i', $row['data']); - if ($row['typeid'] === '~offline-length') { + if ($row['typeid'] === Statistics::OFFLINE_LENGTH) { $row['glyph'] = 'off'; $color = '#444'; + } elseif ($row['typeid'] === Statistics::SUSPEND_LENGTH) { + $row['glyph'] = 'pause'; + $color = '#686'; } else { $row['glyph'] = 'user'; $color = '#e77'; @@ -967,8 +972,26 @@ class Page_Statistics extends Page } if ($client['state'] === 'OCCUPIED') { $spans['graph'] .= '
 
'; + $spans['rows'][] = [ + 'from' => Util::prettyTime($client['logintime']), + 'duration' => '-', + 'glyph' => 'user', + ]; + $row['duration'] = floor($row['data'] / 86400) . 'd ' . gmdate('H:i', $row['data']); } elseif ($client['state'] === 'OFFLINE') { $spans['graph'] .= '
 
'; + $spans['rows'][] = [ + 'from' => Util::prettyTime($client['lastseen']), + 'duration' => '-', + 'glyph' => 'off', + ]; + } elseif ($client['state'] === 'STANDBY') { + $spans['graph'] .= '
 
'; + $spans['rows'][] = [ + 'from' => Util::prettyTime($client['lastseen']), + 'duration' => '-', + 'glyph' => 'pause', + ]; } $t = explode('-', date('Y-n-j-G', $cutoff)); if ($t[3] >= 8 && $t[3] <= 22) { -- cgit v1.2.3-55-g7522 From 9ae2c70073490a253ccf2fcb567d57b07811389b Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 24 Jan 2019 17:31:38 +0100 Subject: [statistics] Minor refactoring (PHP >= 5.6) --- modules-available/statistics/inc/filter.inc.php | 16 ++++--- modules-available/statistics/page.inc.php | 58 ++++++++++++------------- 2 files changed, 38 insertions(+), 36 deletions(-) (limited to 'modules-available/statistics/page.inc.php') diff --git a/modules-available/statistics/inc/filter.inc.php b/modules-available/statistics/inc/filter.inc.php index 3ccea2c3..46de467b 100644 --- a/modules-available/statistics/inc/filter.inc.php +++ b/modules-available/statistics/inc/filter.inc.php @@ -14,6 +14,13 @@ class Filter public $operator; public $argument; + private static $keyCounter = 0; + + public static function getNewKey($colname) + { + return $colname . '_' . (self::$keyCounter++); + } + public function __construct($column, $operator, $argument = null) { $this->column = trim($column); @@ -24,8 +31,7 @@ class Filter /* returns a where clause and adds needed operators to the passed array */ public function whereClause(&$args, &$joins) { - global $unique_key; - $key = $this->column . '_arg' . ($unique_key++); + $key = Filter::getNewKey($this->column); $addendum = ''; /* check if we have to do some parsing*/ @@ -226,8 +232,7 @@ class StateFilter extends Filter $map = [ 'on' => ['IDLE', 'OCCUPIED'], 'off' => ['OFFLINE'], 'idle' => ['IDLE'], 'occupied' => ['OCCUPIED'], 'standby' => ['STANDBY'] ]; $neg = $this->operator == '!=' ? 'NOT ' : ''; if (array_key_exists($this->argument, $map)) { - global $unique_key; - $key = $this->column . '_arg' . ($unique_key++); + $key = Filter::getNewKey($this->column); $args[$key] = $map[$this->argument]; return " machine.state $neg IN ( :$key ) "; } else { @@ -259,8 +264,7 @@ class LocationFilter extends Filter if ($this->argument === 0) { return "machine.locationid IS $neg NULL"; } else { - global $unique_key; - $key = $this->column . '_arg' . ($unique_key++); + $key = Filter::getNewKey($this->column); if ($recursive) { $args[$key] = array_keys(Location::getRecursiveFlat($this->argument)); } else { diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php index 2d86615e..c9a0cac5 100644 --- a/modules-available/statistics/page.inc.php +++ b/modules-available/statistics/page.inc.php @@ -1,7 +1,6 @@ =', '=', '<', '>']; + const OP_STRCMP = ['!~', '~', '=', '!=']; public static $columns; private $query; @@ -27,123 +26,120 @@ class Page_Statistics extends Page */ private $haveSubpage; - /* PHP sucks, no static, const array definitions... Or am I missing something? */ - public function initConstants() + /** + * Do this here instead of const since we need to check for available modules while building array. + */ + public static function initConstants() { - Page_Statistics::$op_nominal = ['!=', '=']; - Page_Statistics::$op_ordinal = ['!=', '<=', '>=', '=', '<', '>']; - Page_Statistics::$op_stringcmp = ['!~', '~', '=', '!=']; Page_Statistics::$columns = [ 'machineuuid' => [ - 'op' => Page_Statistics::$op_nominal, + 'op' => Page_Statistics::OP_NOMINAL, 'type' => 'string', 'column' => true, ], 'macaddr' => [ - 'op' => Page_Statistics::$op_nominal, + 'op' => Page_Statistics::OP_NOMINAL, 'type' => 'string', 'column' => true, ], 'firstseen' => [ - 'op' => Page_Statistics::$op_ordinal, + 'op' => Page_Statistics::OP_ORDINAL, 'type' => 'date', 'column' => true, ], 'lastseen' => [ - 'op' => Page_Statistics::$op_ordinal, + 'op' => Page_Statistics::OP_ORDINAL, 'type' => 'date', 'column' => true, ], 'logintime' => [ - 'op' => Page_Statistics::$op_ordinal, + 'op' => Page_Statistics::OP_ORDINAL, 'type' => 'date', 'column' => true, ], 'realcores' => [ - 'op' => Page_Statistics::$op_ordinal, + 'op' => Page_Statistics::OP_ORDINAL, 'type' => 'int', 'column' => true, ], 'systemmodel' => [ - 'op' => Page_Statistics::$op_stringcmp, + 'op' => Page_Statistics::OP_STRCMP, 'type' => 'string', 'column' => true, ], 'cpumodel' => [ - 'op' => Page_Statistics::$op_stringcmp, + 'op' => Page_Statistics::OP_STRCMP, 'type' => 'string', 'column' => true, ], 'hddgb' => [ - 'op' => Page_Statistics::$op_ordinal, + 'op' => Page_Statistics::OP_ORDINAL, 'type' => 'int', 'column' => false, 'map_sort' => 'id44mb' ], 'gbram' => [ - 'op' => Page_Statistics::$op_ordinal, + 'op' => Page_Statistics::OP_ORDINAL, 'type' => 'int', 'map_sort' => 'mbram', 'column' => false, ], 'kvmstate' => [ - 'op' => Page_Statistics::$op_nominal, + 'op' => Page_Statistics::OP_NOMINAL, 'type' => 'enum', 'column' => true, 'values' => ['ENABLED', 'DISABLED', 'UNSUPPORTED'] ], 'badsectors' => [ - 'op' => Page_Statistics::$op_ordinal, + 'op' => Page_Statistics::OP_ORDINAL, 'type' => 'int', 'column' => true ], 'clientip' => [ - 'op' => Page_Statistics::$op_nominal, + 'op' => Page_Statistics::OP_NOMINAL, 'type' => 'string', 'column' => true ], 'hostname' => [ - 'op' => Page_Statistics::$op_stringcmp, + 'op' => Page_Statistics::OP_STRCMP, 'type' => 'string', 'column' => true ], 'subnet' => [ - 'op' => Page_Statistics::$op_nominal, + 'op' => Page_Statistics::OP_NOMINAL, 'type' => 'string', 'column' => false ], 'currentuser' => [ - 'op' => Page_Statistics::$op_nominal, + 'op' => Page_Statistics::OP_NOMINAL, 'type' => 'string', 'column' => true ], 'state' => [ - 'op' => Page_Statistics::$op_nominal, + 'op' => Page_Statistics::OP_NOMINAL, 'type' => 'enum', 'column' => true, 'values' => ['occupied', 'on', 'off', 'idle', 'standby'] ], 'runtime' => [ - 'op' => Page_Statistics::$op_ordinal, + 'op' => Page_Statistics::OP_NOMINAL, 'type' => 'int', 'column' => true ], ]; if (Module::isAvailable('locations')) { Page_Statistics::$columns['location'] = [ - 'op' => Page_Statistics::$op_stringcmp, + 'op' => Page_Statistics::OP_STRCMP, 'type' => 'enum', 'column' => false, 'values' => array_keys(Location::getLocationsAssoc()), ]; } - /* TODO ... */ } protected function doPreprocess() { - $this->initConstants(); User::load(); if (!User::isLoggedIn()) { Message::addError('main.no-permission'); @@ -1127,3 +1123,5 @@ class Page_Statistics extends Page ), true); } } + +Page_Statistics::initConstants(); -- cgit v1.2.3-55-g7522 From 3b1df888c963d906005ff139892df06a0d1ae2b4 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 28 Jan 2019 13:48:35 +0100 Subject: [statistics] Track and display memory/tmp usage of clients --- modules-available/statistics/api.inc.php | 13 ++++++-- modules-available/statistics/hooks/cron.inc.php | 9 ++++-- modules-available/statistics/install.inc.php | 17 ++++++++++ .../statistics/lang/de/template-tags.json | 4 +++ .../statistics/lang/en/template-tags.json | 4 +++ modules-available/statistics/page.inc.php | 37 ++++++++++++++++------ modules-available/statistics/style.css | 32 +++++++++++++++++++ .../statistics/templates/filterbox.html | 5 ++- .../statistics/templates/machine-main.html | 33 ++++++++++++++++--- 9 files changed, 135 insertions(+), 19 deletions(-) (limited to 'modules-available/statistics/page.inc.php') diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php index 813e7d54..57c4cee2 100644 --- a/modules-available/statistics/api.inc.php +++ b/modules-available/statistics/api.inc.php @@ -34,7 +34,8 @@ if ($type{0} === '~') { // External mode of operation? $mode = Request::post('mode', false, 'string'); $NOW = time(); - $old = Database::queryFirst('SELECT clientip, logintime, lastseen, lastboot, state, mbram, cpumodel FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid)); + $old = Database::queryFirst('SELECT clientip, logintime, lastseen, lastboot, state, mbram, cpumodel, live_memfree, live_swapfree, live_tmpfree + FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid)); if ($old !== false) { settype($old['logintime'], 'integer'); settype($old['lastseen'], 'integer'); @@ -114,6 +115,7 @@ if ($type{0} === '~') { . ' cpumodel = :cpumodel,' . ' systemmodel = :systemmodel,' . ' id44mb = :id44mb,' + . ' live_tmpsize = 0, live_swapsize = 0, live_memsize = 0,' . ' badsectors = :badsectors,' . ' data = :data,' . ' state = :state ' @@ -152,7 +154,10 @@ if ($type{0} === '~') { // Log potential crash if ($old['state'] === 'IDLE' || $old['state'] === 'OCCUPIED') { - writeClientLog('machine-mismatch-poweron', 'Client sent poweron event, but previous known state is ' . $old['state']); + writeClientLog('machine-mismatch-poweron', 'Poweron event, but previous known state is ' . $old['state'] + . '. RAM: ' . Util::readableFileSize($old['live_memfree'], -1, 2) + . ', Swap: ' . Util::readableFileSize($old['live_swapfree'], -1, 2) + . ', ID44: ' . Util::readableFileSize($old['live_memfree'], -1, 2)); } } @@ -190,6 +195,10 @@ if ($type{0} === '~') { } } } + foreach (['memsize', 'tmpsize', 'swapsize', 'memfree', 'tmpfree', 'swapfree'] as $item) { + $strUpdateBoottime .= ' live_' . $item . ' = :_' . $item . ', '; + $params['_' . $item] = ceil(Request::post($item, 0, 'int') / 1024); + } // Figure out what's happening - state changes if ($used === 0 && $old['state'] !== 'IDLE') { if ($old['state'] === 'OCCUPIED' && $sessionLength === 0) { diff --git a/modules-available/statistics/hooks/cron.inc.php b/modules-available/statistics/hooks/cron.inc.php index 0b8e740e..f22d0475 100644 --- a/modules-available/statistics/hooks/cron.inc.php +++ b/modules-available/statistics/hooks/cron.inc.php @@ -24,14 +24,17 @@ function state_cleanup() $standby = time() - 86400 * 4; // Reset standby machines after four days $on = time() - 610; // Reset others after ~10 minutes // Query for logging - $res = Database::simpleQuery("SELECT machineuuid, clientip, state, logintime, lastseen FROM machine - WHERE lastseen < If(state = 'STANDBY', $standby, $on) AND state <> 'OFFLINE'"); + $res = Database::simpleQuery("SELECT machineuuid, clientip, state, logintime, lastseen, live_memfree, live_swapfree, live_tmpfree + FROM machine WHERE lastseen < If(state = 'STANDBY', $standby, $on) AND state <> 'OFFLINE'"); while ($row = $res->fetch(PDO::FETCH_ASSOC)) { Database::exec('INSERT INTO clientlog (dateline, logtypeid, clientip, machineuuid, description, extra) VALUES (UNIX_TIMESTAMP(), :type, :client, :uuid, :description, :longdesc)', array( 'type' => 'machine-mismatch-cron', 'client' => $row['clientip'], - 'description' => 'Client timed out, last known state is ' . $row['state'], + 'description' => 'Client timed out, last known state is ' . $row['state'] + . '. RAM: ' . Util::readableFileSize($row['live_memfree'], -1, 2) + . ', Swap: ' . Util::readableFileSize($row['live_swapfree'], -1, 2) + . ', ID44: ' . Util::readableFileSize($row['live_memfree'], -1, 2), 'longdesc' => '', 'uuid' => $row['machineuuid'], )); diff --git a/modules-available/statistics/install.inc.php b/modules-available/statistics/install.inc.php index 4e2dfcca..84e038a4 100644 --- a/modules-available/statistics/install.inc.php +++ b/modules-available/statistics/install.inc.php @@ -234,5 +234,22 @@ if (!tableHasColumn('machine', 'state')) { $res[] = UPDATE_DONE; } +// 2019-01-25: Add memory/temp stats column +if (!tableHasColumn('machine', 'live_tmpsize')) { + $ret = Database::exec("ALTER TABLE `machine` + ADD COLUMN `live_tmpsize` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `id44mb`, + ADD COLUMN `live_tmpfree` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `live_tmpsize`, + ADD COLUMN `live_swapsize` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `live_tmpfree`, + ADD COLUMN `live_swapfree` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `live_swapsize`, + ADD COLUMN `live_memsize` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `live_swapfree`, + ADD COLUMN `live_memfree` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `live_memsize`, + ADD INDEX `live_tmpfree` (`live_tmpfree`), + ADD INDEX `live_memfree` (`live_memfree`)"); + if ($ret === false) { + finalResponse(UPDATE_FAILED, 'Adding state column to machine table failed: ' . Database::lastError()); + } + $res[] = UPDATE_DONE; +} + // Create response responseFromArray($res); diff --git a/modules-available/statistics/lang/de/template-tags.json b/modules-available/statistics/lang/de/template-tags.json index 84c4690c..2567eea1 100644 --- a/modules-available/statistics/lang/de/template-tags.json +++ b/modules-available/statistics/lang/de/template-tags.json @@ -14,6 +14,7 @@ "lang_event": "Ereignis", "lang_eventType": "Typ", "lang_firstSeen": "Erste Aktivit\u00e4t", + "lang_free": "frei", "lang_gbRam": "RAM", "lang_hardwareSummary": "Hardware", "lang_hdds": "Festplatten", @@ -40,6 +41,7 @@ "lang_machineStandby": "Im Standby", "lang_machineSummary": "Zusammenfassung", "lang_maximumAbbrev": "Max.", + "lang_memFree": "RAM frei (MB)", "lang_memoryStats": "Arbeitsspeicher", "lang_model": "Modell", "lang_modelCount": "Anzahl", @@ -82,10 +84,12 @@ "lang_subnet": "Subnetz", "lang_sureDeletePermanent": "M\u00f6chten Sie diese(n) Rechner wirklich unwiderruflich aus der Datenbank entfernen?\r\n\r\nWichtig: L\u00f6schen verhindert nicht, dass ein Rechner nach erneutem Starten von bwLehrpool wieder in die Datenbank aufgenommen wird.", "lang_sureReplaceNoUndo": "Wollen Sie die Daten ausgew\u00e4hlten Rechner \u00fcbertragen? Diese Aktion kann nicht r\u00fcckg\u00e4ngig gemacht werden.", + "lang_swapFree": "swap frei (MB)", "lang_tempPart": "Temp. Partition", "lang_tempPartStats": "Tempor\u00e4re Partition", "lang_thoseAreProjectors": "Diese Modellnamen werden als Beamer behandelt, auch wenn die EDID-Informationen des Ger\u00e4tes anderes berichten.", "lang_timebarDesc": "Visuelle Darstellung der letzten Tage. Rote Abschnitte zeigen, wann der Rechner belegt war, gr\u00fcne, wann er nicht verwendet wurde, aber eingeschaltet war. Die leicht abgedunkelten Abschnitte markieren N\u00e4chte (22 bis 8 Uhr).", + "lang_tmpFree": "ID44 frei (MB)", "lang_tmpGb": "Temp-HDD", "lang_total": "Gesamt", "lang_usageDetails": "Nutzungsdetails", diff --git a/modules-available/statistics/lang/en/template-tags.json b/modules-available/statistics/lang/en/template-tags.json index b064ee50..1d9cd4da 100644 --- a/modules-available/statistics/lang/en/template-tags.json +++ b/modules-available/statistics/lang/en/template-tags.json @@ -14,6 +14,7 @@ "lang_event": "Event", "lang_eventType": "Type", "lang_firstSeen": "First seen", + "lang_free": "free", "lang_gbRam": "RAM", "lang_hardwareSummary": "Hardware", "lang_hdds": "Hard disk drives", @@ -40,6 +41,7 @@ "lang_machineStandby": "In standby mode", "lang_machineSummary": "Summary", "lang_maximumAbbrev": "max.", + "lang_memFree": "RAM free (MB)", "lang_memoryStats": "Memory", "lang_model": "Model", "lang_modelCount": "Count", @@ -82,10 +84,12 @@ "lang_subnet": "Subnet", "lang_sureDeletePermanent": "Are your sure you want to delete the selected machine(s) from the database? This cannot be undone.\r\n\r\nNote: Deleting machines from the database does not prevent booting up bwLehrpool again, which would recreate their respective database entries.", "lang_sureReplaceNoUndo": "Are you sure you want to replace the selected machine pairs? This action cannot be undone.", + "lang_swapFree": "swap free (MB)", "lang_tempPart": "Temp. partition", "lang_tempPartStats": "Temporary partition", "lang_thoseAreProjectors": "These model names will always be treated as beamers, even if the device's EDID data says otherwise.", "lang_timebarDesc": "Visual representation of the last few days. Red parts mark periods where the client was occupied, green parts where the client was idle. Dimmed parts mark nights (10pm to 8am).", + "lang_tmpFree": "ID44 free (MB)", "lang_tmpGb": "Temp HDD", "lang_total": "Total", "lang_usageDetails": "Detailed usage", diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php index c9a0cac5..a9cde6fb 100644 --- a/modules-available/statistics/page.inc.php +++ b/modules-available/statistics/page.inc.php @@ -122,8 +122,18 @@ class Page_Statistics extends Page 'column' => true, 'values' => ['occupied', 'on', 'off', 'idle', 'standby'] ], - 'runtime' => [ - 'op' => Page_Statistics::OP_NOMINAL, + 'live_swapfree' => [ + 'op' => Page_Statistics::OP_ORDINAL, + 'type' => 'int', + 'column' => true + ], + 'live_memfree' => [ + 'op' => Page_Statistics::OP_ORDINAL, + 'type' => 'int', + 'column' => true + ], + 'live_tmpfree' => [ + 'op' => Page_Statistics::OP_ORDINAL, 'type' => 'int', 'column' => true ], @@ -764,9 +774,9 @@ class Page_Statistics extends Page $row['currentsession'] = $lecture['displayname']; $row['lectureid'] = $lecture['lectureid']; } + $row['session'] = $row['currentsession']; + return; } - $row['session'] = $row['currentsession']; - return; } $res = Database::simpleQuery('SELECT dateline, username, data FROM statistic' . " WHERE clientip = :ip AND typeid = '.vmchooser-session-name'" @@ -783,14 +793,17 @@ class Page_Statistics extends Page } if ($session !== false) { $row['session'] = $session['data']; - $row['username'] = $session['username']; + if (empty($row['currentuser'])) { + $row['username'] = $session['username']; + } } } private function showMachine($uuid) { - $client = Database::queryFirst('SELECT machineuuid, locationid, macaddr, clientip, firstseen, lastseen, logintime, lastboot, state,' - . ' mbram, kvmstate, cpumodel, id44mb, data, hostname, currentuser, currentsession, notes FROM machine WHERE machineuuid = :uuid', + $client = Database::queryFirst('SELECT machineuuid, locationid, macaddr, clientip, firstseen, lastseen, logintime, lastboot, state, + mbram, live_tmpsize, live_tmpfree, live_swapsize, live_swapfree, live_memsize, live_memfree, + kvmstate, cpumodel, id44mb, data, hostname, currentuser, currentsession, notes FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid)); if ($client === false) { Message::addError('unknown-machine', $uuid); @@ -826,6 +839,7 @@ class Page_Statistics extends Page $client['state_' . $client['state']] = true; $client['firstseen_s'] = date('d.m.Y H:i', $client['firstseen']); $client['lastseen_s'] = date('d.m.Y H:i', $client['lastseen']); + $client['logintime_s'] = date('d.m.Y H:i', $client['logintime']); if ($client['lastboot'] == 0) { $client['lastboot_s'] = '-'; } else { @@ -835,9 +849,14 @@ class Page_Statistics extends Page $client['lastboot_s'] .= ' (Up ' . floor($uptime / 86400) . 'd ' . gmdate('H:i', $uptime) . ')'; } } - $client['logintime_s'] = date('d.m.Y H:i', $client['logintime']); - $client['gbram'] = round(round($client['mbram'] / 500) / 2, 1); + $client['gbram'] = round(ceil($client['mbram'] / 512) / 2, 1); $client['gbtmp'] = round($client['id44mb'] / 1024); + foreach (['tmp', 'swap', 'mem'] as $item) { + if ($client['live_' . $item . 'size'] == 0) + continue; + $client['live_' . $item . 'percent'] = round(($client['live_' . $item . 'free'] / $client['live_' . $item . 'size']) * 100, 2); + $client['live_' . $item . 'free_s'] = Util::readableFileSize($client['live_' . $item . 'free'], -1, 2); + } $client['ramclass'] = $this->ramColorClass($client['mbram']); $client['kvmclass'] = $this->kvmColorClass($client['kvmstate']); $client['hddclass'] = $this->hddColorClass($client['gbtmp']); diff --git a/modules-available/statistics/style.css b/modules-available/statistics/style.css index 1496ac87..c48275ba 100644 --- a/modules-available/statistics/style.css +++ b/modules-available/statistics/style.css @@ -8,4 +8,36 @@ border-radius: 25px; font-size: 20px; line-height: 40px; +} + +.meter { + position: relative; + border-radius: 5px; + border: 1px solid #999; + background: linear-gradient(to right, #9e6 0%, #fb8 66%, #f32 100%); + height: 1.25em; + padding: 0; + width: 100%; + overflow: hidden; +} + +.meter .bar { + position: absolute; + top: 0; + height: 100%; + right: 0; + background: #efe; + display: inline-block; + padding: 0; + margin: 0 0 0 auto; +} + +.meter .text { + position: absolute; + right: 5px; + overflow: visible; + font-size: 8pt; + white-space: nowrap; + z-index: 1000; + text-shadow: #fff 1px 1px; } \ No newline at end of file diff --git a/modules-available/statistics/templates/filterbox.html b/modules-available/statistics/templates/filterbox.html index cd8ec24d..07aa7320 100644 --- a/modules-available/statistics/templates/filterbox.html +++ b/modules-available/statistics/templates/filterbox.html @@ -101,7 +101,10 @@ var slxFilterNames = { currentuser: '{{lang_currentUser}}', subnet: '{{lang_subnet}}', runtime: '{{lang_runtimeHours}}', - hostname: '{{lang_hostname}}' + hostname: '{{lang_hostname}}', + live_swapfree: '{{lang_swapFree}}', + live_memfree: '{{lang_memFree}}', + live_tmpfree: '{{lang_tmpFree}}' }; slxLocations = {{{locations}}}; diff --git a/modules-available/statistics/templates/machine-main.html b/modules-available/statistics/templates/machine-main.html index f1021482..904c780b 100644 --- a/modules-available/statistics/templates/machine-main.html +++ b/modules-available/statistics/templates/machine-main.html @@ -1,3 +1,4 @@ +{{%FILTERS}}

{{hostname}} {{#hostname}}–{{/hostname}} {{clientip}} {{#notes}}{{/notes}} @@ -117,9 +118,23 @@ {{lang_ram}} - {{gbram}} GiB - {{#maxram}}({{lang_maximumAbbrev}} {{maxram}}){{/maxram}} - {{ramtype}} +
+ {{gbram}} GiB + {{#maxram}}({{lang_maximumAbbrev}} {{maxram}}){{/maxram}} + {{ramtype}} +
+ {{#live_memsize}} +
+
{{live_memfree_s}} {{lang_free}}
+
+
+ {{/live_memsize}} + {{#live_swapsize}} +
+
{{live_swapfree_s}} {{lang_free}}
+
+
+ {{/live_swapsize}} {{#extram}} @@ -135,7 +150,17 @@ {{/extram}} {{lang_tempPart}} - {{gbtmp}} GiB + +
+ {{gbtmp}} GiB +
+ {{#live_tmpsize}} +
+
{{live_tmpfree_s}} {{lang_free}}
+
+
+ {{/live_tmpsize}} + {{lang_64bitSupport}} -- cgit v1.2.3-55-g7522