summaryrefslogtreecommitdiffstats
path: root/modules-available/statistics
diff options
context:
space:
mode:
authorroot2019-02-19 18:53:50 +0100
committerroot2019-02-19 18:53:50 +0100
commit0ad4c0f8196b61699754762aacbaab0223478ab9 (patch)
treede434c4aea8d07ecd01cd3badd48d057d62c2d1b /modules-available/statistics
parent[usb-lock-off] Edit rule cleanup and fix of the dropdown boxes. (diff)
parent[statistics] Fix RAM change warning to handle increase too (diff)
downloadslx-admin-usb-lock-off.tar.gz
slx-admin-usb-lock-off.tar.xz
slx-admin-usb-lock-off.zip
Merge branch 'master' into usb-lock-offusb-lock-off
Diffstat (limited to 'modules-available/statistics')
-rw-r--r--modules-available/statistics/api.inc.php137
-rw-r--r--modules-available/statistics/config.json12
-rw-r--r--modules-available/statistics/hooks/cron.inc.php32
-rw-r--r--modules-available/statistics/inc/filter.inc.php49
-rw-r--r--modules-available/statistics/inc/filterset.inc.php82
-rw-r--r--modules-available/statistics/inc/parser.inc.php36
-rw-r--r--modules-available/statistics/inc/statistics.inc.php17
-rw-r--r--modules-available/statistics/install.inc.php17
-rw-r--r--modules-available/statistics/lang/de/permissions.json13
-rw-r--r--modules-available/statistics/lang/de/template-tags.json4
-rw-r--r--modules-available/statistics/lang/en/permissions.json13
-rw-r--r--modules-available/statistics/lang/en/template-tags.json4
-rw-r--r--modules-available/statistics/page.inc.php267
-rw-r--r--modules-available/statistics/pages/projectors.inc.php4
-rw-r--r--modules-available/statistics/permissions/permissions.json31
-rw-r--r--modules-available/statistics/style.css32
-rw-r--r--modules-available/statistics/templates/clientlist.html25
-rw-r--r--modules-available/statistics/templates/cpumodels.html4
-rw-r--r--modules-available/statistics/templates/filterbox.html17
-rw-r--r--modules-available/statistics/templates/id44.html2
-rw-r--r--modules-available/statistics/templates/kvmstate.html2
-rw-r--r--modules-available/statistics/templates/machine-hdds.html6
-rw-r--r--modules-available/statistics/templates/machine-main.html44
-rw-r--r--modules-available/statistics/templates/machine-notes.html7
-rw-r--r--modules-available/statistics/templates/memory.html2
-rw-r--r--modules-available/statistics/templates/summary.html4
26 files changed, 606 insertions, 257 deletions
diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php
index a7a636b3..d4b8f346 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 '
@@ -123,32 +125,20 @@ if ($type{0} === '~') {
}
}
// Maybe log old crashed session
- if ($uptime < 120) {
+ if ($uptime < 150 && $old !== false) {
// See if we have a lingering session, create statistic entry if so
- if ($old !== false && $old['logintime'] !== 0) {
+ if ($old['state'] === 'OCCUPIED' && $old['logintime'] !== 0) {
$sessionLength = $old['lastseen'] - $old['logintime'];
if ($sessionLength > 30 && $sessionLength < 86400*2) {
- Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)'
- . " VALUES (:start, '~session-length', :uuid, :clientip, '', :length)", array(
- 'start' => $old['logintime'],
- 'uuid' => $uuid,
- 'clientip' => $ip,
- 'length' => $sessionLength
- ));
+ Statistics::logMachineState($uuid, $ip, Statistics::SESSION_LENGTH, $old['logintime'], $sessionLength);
}
}
// Write poweroff period length to statistic table
- if ($old !== false && $old['lastseen'] !== 0) {
+ if ($old['lastseen'] !== 0) {
$lastSeen = $old['lastseen'];
$offtime = ($NOW - $uptime) - $lastSeen;
- if ($offtime > 300 && $offtime < 86400 * 90) {
- Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)'
- . " VALUES (:shutdown, '~offline-length', :uuid, :clientip, '', :length)", array(
- 'shutdown' => $lastSeen,
- 'uuid' => $uuid,
- 'clientip' => $ip,
- 'length' => $offtime
- ));
+ if ($offtime > 90 && $offtime < 86400 * 30) {
+ Statistics::logMachineState($uuid, $ip, $old['state'] === 'STANDBY' ? Statistics::SUSPEND_LENGTH : Statistics::OFFLINE_LENGTH, $lastSeen, $offtime);
}
}
}
@@ -161,6 +151,14 @@ if ($type{0} === '~') {
// Check for suspicious hardware changes
if ($old !== false) {
checkHardwareChange($old, $new);
+
+ // Log potential crash
+ if ($old['state'] === 'IDLE' || $old['state'] === 'OCCUPIED') {
+ 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_tmpfree'], -1, 2));
+ }
}
// Write statistics data
@@ -168,36 +166,45 @@ if ($type{0} === '~') {
} else if ($type === '~runstate') {
// Usage (occupied/free)
$sessionLength = 0;
+ $strUpdateBoottime = '';
if ($old === false) die("Unknown machine.\n");
if ($old['clientip'] !== $ip) {
EventLog::warning("[runstate] IP address of client $uuid seems to have changed ({$old['clientip']} -> $ip)");
die("Address changed.\n");
}
$used = Request::post('used', 0, 'integer');
- if ($old['state'] === 'OFFLINE' && $NOW - $old['lastseen'] > 600) {
- $strUpdateBoottime = ' lastboot = UNIX_TIMESTAMP(), ';
- } else {
- $strUpdateBoottime = '';
- }
- // 1) Log last session length if we didn't see the machine for a while
- if ($NOW - $old['lastseen'] > 610 && $old['lastseen'] !== 0) {
- // Old session timed out - might be caused by hard reboot
- if ($old['logintime'] !== 0) {
- if ($old['lastseen'] > $old['logintime']) {
- $sessionLength = $old['lastseen'] - $old['logintime'];
- }
- $old['logintime'] = 0;
- }
- }
- // Figure out what's happening - state changes
$params = array(
'uuid' => $uuid,
'oldlastseen' => $old['lastseen'],
'oldstate' => $old['state'],
);
+ if ($old['state'] === 'OFFLINE') {
+ // This should never happen -- we expect a poweron event before runstate, which would set the state to IDLE
+ // So it might be that the poweron event got lost, or that a couple of runstate events got lost, which
+ // caused our cron.inc.php to time out the client and reset it to OFFLINE
+ if ($NOW - $old['lastseen'] > 900) {
+ $strUpdateBoottime = ' lastboot = UNIX_TIMESTAMP(), ';
+ }
+ // 1) Log last session length if we didn't see the machine for a while
+ if ($NOW - $old['lastseen'] > 900 && $old['lastseen'] !== 0) {
+ // Old session timed out - might be caused by hard reboot
+ if ($old['logintime'] !== 0) {
+ if ($old['lastseen'] > $old['logintime']) {
+ $sessionLength = $old['lastseen'] - $old['logintime'];
+ }
+ }
+ }
+ }
+ 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') {
- // Is not in use, was in use before
- $sessionLength = $NOW - $old['logintime'];
+ if ($old['state'] === 'OCCUPIED' && $sessionLength === 0) {
+ // Is not in use, was in use before
+ $sessionLength = $NOW - $old['logintime'];
+ }
$res = Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),'
. $strUpdateBoottime
. " logintime = 0, currentuser = NULL, state = 'IDLE' "
@@ -227,13 +234,7 @@ if ($type{0} === '~') {
}
// 9) Log last session length if applicable
if ($mode === false && $sessionLength > 0 && $sessionLength < 86400*2 && $old['logintime'] !== 0) {
- Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)'
- . " VALUES (:start, '~session-length', :uuid, :clientip, '', :length)", array(
- 'start' => $old['logintime'],
- 'uuid' => $uuid,
- 'clientip' => $ip,
- 'length' => $sessionLength
- ));
+ Statistics::logMachineState($uuid, $ip, Statistics::SESSION_LENGTH, $old['logintime'], $sessionLength);
}
} elseif ($type === '~poweroff') {
if ($old === false) die("Unknown machine.\n");
@@ -241,16 +242,10 @@ if ($type{0} === '~') {
EventLog::warning("[poweroff] IP address of client $uuid seems to have changed ({$old['clientip']} -> $ip)");
die("Address changed.\n");
}
- if ($mode === false && $old['logintime'] !== 0) {
+ if ($mode === false && $old['state'] === 'OCCUPIED' && $old['logintime'] !== 0) {
$sessionLength = $old['lastseen'] - $old['logintime'];
if ($sessionLength > 0 && $sessionLength < 86400*2) {
- Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)'
- . " VALUES (:start, '~session-length', :uuid, :clientip, '', :length)", array(
- 'start' => $old['logintime'],
- 'uuid' => $uuid,
- 'clientip' => $ip,
- 'length' => $sessionLength
- ));
+ Statistics::logMachineState($uuid, $ip, Statistics::SESSION_LENGTH, $old['logintime'], $sessionLength);
}
}
Database::exec("UPDATE machine SET logintime = 0, lastseen = UNIX_TIMESTAMP(), state = 'OFFLINE'
@@ -267,6 +262,10 @@ if ($type{0} === '~') {
foreach ($screens as $port => $screen) {
if (!array_key_exists('name', $screen))
continue;
+ // Filter bogus data
+ $screen['name'] = iconv('UTF-8', 'UTF-8//IGNORE', $screen['name']);
+ if (empty($screen['name']))
+ continue;
if (array_key_exists($screen['name'], $hwids)) {
$hwid = $hwids[$screen['name']];
} else {
@@ -359,13 +358,7 @@ if ($type{0} === '~') {
$lastSeen = $old['lastseen'];
$duration = $NOW - $lastSeen;
if ($duration > 500 && $duration < 86400 * 14) {
- Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)'
- . " VALUES (:suspend, '~suspend-length', :uuid, :clientip, '', :length)", array(
- 'suspend' => $lastSeen,
- 'uuid' => $uuid,
- 'clientip' => $ip,
- 'length' => $duration
- ));
+ Statistics::logMachineState($uuid, $ip, Statistics::SUSPEND_LENGTH, $lastSeen, $duration);
}
}
} else {
@@ -399,6 +392,18 @@ function writeStatisticLog($type, $username, $data)
));
}
+function writeClientLog($type, $description)
+{
+ global $ip, $uuid;
+ Database::exec('INSERT INTO clientlog (dateline, logtypeid, clientip, machineuuid, description, extra) VALUES (UNIX_TIMESTAMP(), :type, :client, :uuid, :description, :longdesc)', array(
+ 'type' => $type,
+ 'client' => $ip,
+ 'description' => $description,
+ 'longdesc' => '',
+ 'uuid' => $uuid,
+ ));
+}
+
// For backwards compat, we require the . prefix
if ($type{0} === '.') {
@@ -428,10 +433,16 @@ if ($type{0} === '.') {
function checkHardwareChange($old, $new)
{
if ($new['mbram'] !== 0) {
- if ($new['mbram'] + 1000 < $old['mbram']) {
- $ram1 = round($old['mbram'] / 512) / 2;
- $ram2 = round($new['mbram'] / 512) / 2;
- EventLog::warning('[poweron] Client ' . $new['uuid'] . ' (' . $new['clientip'] . "): RAM decreased from {$ram1}GB to {$ram2}GB");
+ if ($new['mbram'] < 6200) {
+ $ram1 = ceil($old['mbram'] / 512) / 2;
+ $ram2 = ceil($new['mbram'] / 512) / 2;
+ } else {
+ $ram1 = ceil($old['mbram'] / 1024);
+ $ram2 = ceil($new['mbram'] / 1024);
+ }
+ if ($ram1 !== $ram2) {
+ $word = $ram1 > $ram2 ? 'decreased' : 'increased';
+ EventLog::warning('[poweron] Client ' . $new['uuid'] . ' (' . $new['clientip'] . "): RAM $word from {$ram1}GB to {$ram2}GB");
}
if (!empty($old['cpumodel']) && !empty($new['cpumodel']) && $new['cpumodel'] !== $old['cpumodel']) {
EventLog::warning('[poweron] Client ' . $new['uuid'] . ' (' . $new['clientip'] . "): CPU changed from '{$old['cpumodel']}' to '{$new['cpumodel']}'");
diff --git a/modules-available/statistics/config.json b/modules-available/statistics/config.json
index 333f881a..412dc3cb 100644
--- a/modules-available/statistics/config.json
+++ b/modules-available/statistics/config.json
@@ -1,5 +1,9 @@
{
- "category":"main.status",
- "dependencies": [ "js_chart", "js_selectize", "bootstrap_datepicker"],
- "permission":"0"
-}
+ "category": "main.status",
+ "dependencies": [
+ "js_chart",
+ "js_selectize",
+ "bootstrap_datepicker"
+ ],
+ "permission": "0"
+} \ No newline at end of file
diff --git a/modules-available/statistics/hooks/cron.inc.php b/modules-available/statistics/hooks/cron.inc.php
index 4df7b0d4..6393b2c6 100644
--- a/modules-available/statistics/hooks/cron.inc.php
+++ b/modules-available/statistics/hooks/cron.inc.php
@@ -21,9 +21,37 @@ function logstats()
function state_cleanup()
{
// Fix online state of machines that crashed
- $standby = time() - 86400 * 2; // Reset standby machines after two days
+ $standby = time() - 86400 * 4; // Reset standby machines after four days
$on = time() - 610; // Reset others after ~10 minutes
- Database::exec("UPDATE machine SET state = 'OFFLINE' WHERE lastseen < If(state = 'STANDBY', $standby, $on) AND state <> 'OFFLINE'");
+ // Query for logging
+ $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']
+ . '. RAM: ' . Util::readableFileSize($row['live_memfree'], -1, 2)
+ . ', Swap: ' . Util::readableFileSize($row['live_swapfree'], -1, 2)
+ . ', ID44: ' . Util::readableFileSize($row['live_tmpfree'], -1, 2),
+ 'longdesc' => '',
+ 'uuid' => $row['machineuuid'],
+ ));
+ if ($row['state'] === 'OCCUPIED') {
+ $length = $row['lastseen'] - $row['logintime'];
+ if ($length > 0 && $length < 86400 * 7) {
+ Statistics::logMachineState($row['machineuuid'], $row['clientip'], Statistics::SESSION_LENGTH, $row['logintime'], $length);
+ }
+ } elseif ($row['state'] === 'STANDBY') {
+ $length = time() - $row['lastseen'];
+ if ($length > 0 && $length < 86400 * 7) {
+ Statistics::logMachineState($row['machineuuid'], $row['clientip'], Statistics::SUSPEND_LENGTH, $row['lastseen'], $length);
+ }
+ }
+ }
+ // Update -- yes this is not atomic. Should be sufficient for simple warnings and bookkeeping though.
+ Database::exec("UPDATE machine SET logintime = 0, state = 'OFFLINE' WHERE lastseen < If(state = 'STANDBY', $standby, $on) AND state <> 'OFFLINE'");
}
state_cleanup();
diff --git a/modules-available/statistics/inc/filter.inc.php b/modules-available/statistics/inc/filter.inc.php
index f6765059..46de467b 100644
--- a/modules-available/statistics/inc/filter.inc.php
+++ b/modules-available/statistics/inc/filter.inc.php
@@ -14,18 +14,24 @@ 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);
$this->operator = trim($operator);
- $this->argument = trim($argument);
+ $this->argument = is_array($argument) ? $argument : trim($argument);
}
/* 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*/
@@ -187,20 +193,24 @@ class Id44Filter extends Filter
public function whereClause(&$args, &$joins)
{
global $SIZE_ID44;
- $lower = floor(Page_Statistics::findBestValue($SIZE_ID44, $this->argument, false) * 1024 - 100);
- $upper = ceil(Page_Statistics::findBestValue($SIZE_ID44, $this->argument, true) * 1024 + 100);
+ if ($this->operator === '=' || $this->operator === '!=') {
+ $lower = floor(Page_Statistics::findBestValue($SIZE_ID44, $this->argument, false) * 1024 - 100);
+ $upper = ceil(Page_Statistics::findBestValue($SIZE_ID44, $this->argument, true) * 1024 + 100);
+ } else {
+ $lower = $upper = round($this->argument * 1024);
+ }
- if ($this->operator == '=') {
+ if ($this->operator === '=') {
return " id44mb BETWEEN $lower AND $upper";
- } elseif ($this->operator == '!=') {
+ } elseif ($this->operator === '!=') {
return " id44mb < $lower OR id44mb > $upper";
- } elseif ($this->operator == '<=') {
- return " id44mb < $upper";
- } elseif ($this->operator == '>=') {
- return " id44mb > $lower";
- } elseif ($this->operator == '<') {
+ } elseif ($this->operator === '<=') {
+ return " id44mb <= $upper";
+ } elseif ($this->operator === '>=') {
+ return " id44mb >= $lower";
+ } elseif ($this->operator === '<') {
return " id44mb < $lower";
- } elseif ($this->operator == '>') {
+ } elseif ($this->operator === '>') {
return " id44mb > $upper";
} else {
error_log("unimplemented operator in Id44Filter: $this->operator");
@@ -222,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 {
@@ -245,13 +254,17 @@ class LocationFilter extends Filter
$recursive = (substr($this->operator, -1) === '~');
$this->operator = str_replace('~', '=', $this->operator);
- settype($this->argument, 'int');
+ if (is_array($this->argument)) {
+ if ($recursive)
+ Util::traceError('Cannot use ~ operator for location with array');
+ } else {
+ settype($this->argument, 'int');
+ }
$neg = $this->operator === '=' ? '' : 'NOT';
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/inc/filterset.inc.php b/modules-available/statistics/inc/filterset.inc.php
index 25c5c8fa..774bfd18 100644
--- a/modules-available/statistics/inc/filterset.inc.php
+++ b/modules-available/statistics/inc/filterset.inc.php
@@ -9,6 +9,8 @@ class FilterSet
private $sortDirection;
private $sortColumn;
+ private $cache = false;
+
public function __construct($filters)
{
$this->filters = $filters;
@@ -16,19 +18,28 @@ class FilterSet
public function setSort($col, $direction)
{
- $this->sortDirection = $direction === 'DESC' ? 'DESC' : 'ASC';
+ $direction = ($direction === 'DESC' ? 'DESC' : 'ASC');
- if (is_string($col) && array_key_exists($col, Page_Statistics::$columns)) {
- $this->sortColumn = $col;
- } else {
+ if (!is_string($col) || !array_key_exists($col, Page_Statistics::$columns)) {
/* default sorting column is clientip */
- $this->sortColumn = 'clientip';
+ $col = 'clientip';
}
-
+ if ($col === $this->sortColumn && $direction === $this->sortDirection)
+ return;
+ $this->cache = false;
+ $this->sortDirection = $direction;
+ $this->sortColumn = $col;
}
public function makeFragments(&$where, &$join, &$sort, &$args)
{
+ if ($this->cache !== false) {
+ $where = $this->cache['where'];
+ $join = $this->cache['join'];
+ $sort = $this->cache['sort'];
+ $args = $this->cache['args'];
+ return;
+ }
/* generate where clause & arguments */
$where = '';
$joins = [];
@@ -54,16 +65,13 @@ class FilterSet
$sort = " ORDER BY " . $concreteCol . " " . $this->sortDirection
. ", machineuuid ASC";
+ $this->cache = compact('where', 'join', 'sort', 'args');
}
public function isNoId44Filter()
{
- foreach ($this->filters as $filter) {
- if (get_class($filter) === 'Id44Filter' && $filter->argument == 0) {
- return true;
- }
- }
- return false;
+ $filter = $this->hasFilter('Id44Filter');
+ return $filter !== false && $filter->argument == 0;
}
public function getSortDirection()
@@ -78,10 +86,58 @@ class FilterSet
public function filterNonClients()
{
- if (Module::get('runmode') === false)
+ if (Module::get('runmode') === false || $this->hasFilter('IsClientFilter') !== false)
return;
+ $this->cache = false;
// Runmode module exists, add filter
$this->filters[] = new IsClientFilter(true);
}
+ /**
+ * @param string $type filter type (class name)
+ * @return false|Filter The filter, false if not found
+ */
+ public function hasFilter($type)
+ {
+ foreach ($this->filters as $filter) {
+ if (get_class($filter) === $type) {
+ return $filter;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add a location filter based on the allowed permissions for the given permission.
+ * Returns false if the user doesn't have the given permission for any location.
+ *
+ * @param string $permission permission to use
+ * @return bool false if no permission for any location, true otherwise
+ */
+ public function setAllowedLocationsFromPermission($permission)
+ {
+ $locs = User::getAllowedLocations($permission);
+ if (empty($locs))
+ return false;
+ if (in_array(0, $locs)) {
+ if (!isset($this->filters['permissions']))
+ return true;
+ unset($this->filters['permissions']);
+ } else {
+ $this->filters['permissions'] = new LocationFilter('=', $locs);
+ }
+ $this->cache = false;
+ return true;
+ }
+
+ /**
+ * @return false|array
+ */
+ public function getAllowedLocations()
+ {
+ if (isset($this->filters['permissions']->argument) && is_array($this->filters['permissions']->argument))
+ return $this->filters['permissions']->argument;
+ return false;
+ }
+
}
diff --git a/modules-available/statistics/inc/parser.inc.php b/modules-available/statistics/inc/parser.inc.php
index 679055a7..0d39079d 100644
--- a/modules-available/statistics/inc/parser.inc.php
+++ b/modules-available/statistics/inc/parser.inc.php
@@ -104,10 +104,12 @@ class Parser {
foreach ($lines as $line) {
if (preg_match('/^Disk (\S+):.* (\d+) bytes/i', $line, $out)) {
// --- Beginning of MBR disk ---
+ unset($hdd);
if ($out[2] < 10000) // sometimes vmware reports lots of 512byte disks
continue;
+ if (substr($out[1], 0, 8) === '/dev/dm-') // Ignore device mapper
+ continue;
// disk total size and name
- unset($hdd);
$mbrToMbFactor = 0; // This is != 0 for mbr
$sectorToMbFactor = 0; // This is != for gpt
$hdd = array(
@@ -122,10 +124,12 @@ class Parser {
$hdds[] = &$hdd;
} elseif (preg_match('/^Disk (\S+):\s+(\d+)\s+sectors,/i', $line, $out)) {
// --- Beginning of GPT disk ---
+ unset($hdd);
if ($out[2] < 1000) // sometimes vmware reports lots of 512byte disks
continue;
+ if (substr($out[1], 0, 8) === '/dev/dm-') // Ignore device mapper
+ continue;
// disk total size and name
- unset($hdd);
$mbrToMbFactor = 0; // This is != 0 for mbr
$sectorToMbFactor = 0; // This is != for gpt
$hdd = array(
@@ -151,24 +155,44 @@ class Parser {
$type = strtolower($out[4]);
if ($type === '5' || $type === 'f' || $type === '85') {
continue;
+ } elseif ($type === '44') {
+ $out[5] = 'OpenSLX-ID44';
+ $color = '#5c1';
+ } elseif ($type === '45') {
+ $out[5] = 'OpenSLX-ID45';
+ $color = '#0d7';
+ } elseif ($type === '82') {
+ $color = '#48f';
+ } else {
+ $color = '#e55';
}
+
$partsize = round(($out[3] - $out[2]) * $mbrToMbFactor);
$hdd['partitions'][] = array(
'id' => $out[1],
'name' => $out[1],
'size' => round($partsize / 1024, $partsize < 1024 ? 1 : 0),
- 'type' => ($type === '44' ? 'OpenSLX' : $out[5]),
+ 'type' => $out[5],
);
$hdd['json'][] = array(
'label' => $out[1],
'value' => $partsize,
- 'color' => ($type === '44' ? '#4d4' : ($type === '82' ? '#48f' : '#e55')),
+ 'color' => $color,
);
$hdd['used'] += $partsize;
} elseif (isset($hdd) && $sectorToMbFactor !== 0 && preg_match(',^\s*(\d+)\s+(\d+)[\+\-]?\s+(\d+)[\+\-]?\s+\S+\s+([0-9a-f]+)\s+(.*)$,i', $line, $out)) {
// --- GPT: Partition entry ---
// Some partition
$type = $out[5];
+ if ($type === 'OpenSLX-ID44') {
+ $color = '#5c1';
+ } elseif ($type === 'OpenSLX-ID45') {
+ $color = '#0d7';
+ } elseif ($type === 'Linux swap') {
+ $color = '#48f';
+ } else {
+ $color = '#e55';
+ }
$id = $hdd['devid'] . '-' . $out[1];
$partsize = round(($out[3] - $out[2]) * $sectorToMbFactor);
$hdd['partitions'][] = array(
@@ -180,7 +204,7 @@ class Parser {
$hdd['json'][] = array(
'label' => $id,
'value' => $partsize,
- 'color' => ($type === 'OpenSLX-ID44' ? '#4d4' : ($type === 'Linux swap' ? '#48f' : '#e55')),
+ 'color' => $color,
);
$hdd['used'] += $partsize;
}
@@ -193,7 +217,7 @@ class Parser {
$hdd['size'] = round(($hdd['sectors'] * $sectorToMbFactor) / 1024);
}
$free = $hdd['size'] - $hdd['used'];
- if ($free > 5 || ($free / $hdd['size']) > 0.1) {
+ if ($hdd['size'] > 0 && ($free > 5 || ($free / $hdd['size']) > 0.1)) {
$hdd['partitions'][] = array(
'id' => 'free-id-' . $i,
'name' => Dictionary::translate('unused'),
diff --git a/modules-available/statistics/inc/statistics.inc.php b/modules-available/statistics/inc/statistics.inc.php
index 2500f16f..1f8a081a 100644
--- a/modules-available/statistics/inc/statistics.inc.php
+++ b/modules-available/statistics/inc/statistics.inc.php
@@ -70,4 +70,21 @@ class Statistics
return $list;
}
+ const SESSION_LENGTH = '~session-length';
+ const OFFLINE_LENGTH = '~offline-length';
+ const SUSPEND_LENGTH = '~suspend-length';
+
+ public static function logMachineState($uuid, $ip, $type, $start, $length, $username = '')
+ {
+ return Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)'
+ . " VALUES (:start, :type, :uuid, :clientip, :username, :length)", array(
+ 'start' => $start,
+ 'type' => $type,
+ 'uuid' => $uuid,
+ 'clientip' => $ip,
+ 'username' => $username,
+ 'length' => $length,
+ ));
+ }
+
}
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/permissions.json b/modules-available/statistics/lang/de/permissions.json
index 15303993..8579b28f 100644
--- a/modules-available/statistics/lang/de/permissions.json
+++ b/modules-available/statistics/lang/de/permissions.json
@@ -1,5 +1,12 @@
{
- "view": "Client Statistiken anschauen.",
- "note": "Client Notizen speichern.",
- "delete": "Client löschen."
+ "hardware.projectors.edit": "Beamerzuweisung bearbeiten",
+ "hardware.projectors.view": "Beamerzuweisung anzeigen",
+ "machine.delete": "Rechner l\u00f6schen.",
+ "machine.note": "Anmerkungen zu einem Rechner speichern.",
+ "machine.note.edit": "Anmerkungen bearbeiten",
+ "machine.note.view": "Anmerkungen anzeigen",
+ "machine.view-details": "Clientinformationen anzeigen",
+ "view": "Statistiken anschauen.",
+ "view.list": "Clientliste anzeigen",
+ "view.summary": "Visualisierung anzeigen"
} \ No newline at end of file
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/permissions.json b/modules-available/statistics/lang/en/permissions.json
index 7be32f22..445f96b8 100644
--- a/modules-available/statistics/lang/en/permissions.json
+++ b/modules-available/statistics/lang/en/permissions.json
@@ -1,5 +1,12 @@
{
- "view": "View client statistics.",
- "note": "Save client notes.",
- "delete": "Delete client."
+ "hardware.projectors.edit": "Edit beamer assignment",
+ "hardware.projectors.view": "Show beamer assignment",
+ "machine.delete": "Delete clients.",
+ "machine.note": "Save client notes.",
+ "machine.note.edit": "Edit notes",
+ "machine.note.view": "Show notes",
+ "machine.view-details": "Show client details",
+ "view": "View statistics.",
+ "view.list": "Show client list",
+ "view.summary": "Show visualization"
} \ No newline at end of file
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 5fe4ebfa..a9cde6fb 100644
--- a/modules-available/statistics/page.inc.php
+++ b/modules-available/statistics/page.inc.php
@@ -1,165 +1,177 @@
<?php
global $STATS_COLORS, $SIZE_ID44, $SIZE_RAM;
-global $unique_key;
$STATS_COLORS = array();
for ($i = 0; $i < 10; ++$i) {
$STATS_COLORS[] = '#55' . sprintf('%02s%02s', dechex((($i + 1) * ($i + 1)) / .3922), dechex(abs((5 - $i) * 51)));
}
//$STATS_COLORS = array('#57e', '#ee8', '#5ae', '#fb7', '#6d7', '#e77', '#3af', '#666', '#e0e', '#999');
-$SIZE_ID44 = array(0, 8, 16, 24, 30, 40, 50, 60, 80, 100, 120, 150, 180, 250, 300, 350, 400, 450, 500);
+$SIZE_ID44 = array(0, 8, 16, 24, 30, 40, 50, 60, 80, 100, 120, 150, 180, 250, 300, 400, 500, 1000, 2000, 4000);
$SIZE_RAM = array(1, 2, 3, 4, 6, 8, 10, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 320, 480, 512, 768, 1024);
class Page_Statistics extends Page
{
/* some constants, TODO: Find a better place */
- public static $op_nominal;
- public static $op_ordinal;
- public static $op_stringcmp;
+ const OP_NOMINAL = ['!=', '='];
+ const OP_ORDINAL = ['!=', '<=', '>=', '=', '<', '>'];
+ const OP_STRCMP = ['!~', '~', '=', '!='];
public static $columns;
private $query;
-
- private $locationsAllowedToView;
+ private $show;
/**
* @var bool whether we have a SubPage from the pages/ subdir
*/
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,
+ '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
],
];
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');
Util::redirect('?do=Main');
}
- $this->locationsAllowedToView = User::getAllowedLocations("view");
-
-
- $show = Request::any('show', 'stat', 'string');
- $show = preg_replace('/[^a-z0-9_\-]/', '', $show);
+ $this->show = Request::any('show', false, 'string');
+ if ($this->show === false) {
+ if (User::hasPermission('view.summary')) {
+ $this->show = 'summary';
+ } elseif (User::hasPermission('view.list')) {
+ $this->show = 'list';
+ } else {
+ User::assertPermission('view.summary');
+ }
+ } else {
+ $this->show = preg_replace('/[^a-z0-9_\-]/', '', $this->show);
+ }
- if (file_exists('modules/statistics/pages/' . $show . '.inc.php')) {
+ if (file_exists('modules/statistics/pages/' . $this->show . '.inc.php')) {
- require_once 'modules/statistics/pages/' . $show . '.inc.php';
+ require_once 'modules/statistics/pages/' . $this->show . '.inc.php';
$this->haveSubpage = true;
SubPage::doPreprocess();
@@ -168,20 +180,23 @@ class Page_Statistics extends Page
$action = Request::post('action');
if ($action === 'setnotes') {
$uuid = Request::post('uuid', '', 'string');
- $locationid = Database::queryFirst('SELECT locationid FROM machine WHERE machineuuid = :uuid',
- array('uuid' => $uuid))['locationid'];
- if (User::hasPermission("note", $locationid)) {
- $text = Request::post('content', '', 'string');
- if (empty($text)) {
- $text = null;
- }
- Database::exec('UPDATE machine SET notes = :text WHERE machineuuid = :uuid', array(
- 'uuid' => $uuid,
- 'text' => $text,
- ));
- Message::addSuccess('notes-saved');
- Util::redirect('?do=Statistics&uuid=' . $uuid);
+ $res = Database::queryFirst('SELECT locationid FROM machine WHERE machineuuid = :uuid',
+ array('uuid' => $uuid));
+ if ($res === false) {
+ Message::addError('unknown-machine', $uuid);
+ Util::redirect('?do=statistics');
}
+ User::assertPermission("machine.note.edit", (int)$res['locationid']);
+ $text = Request::post('content', null, 'string');
+ if (empty($text)) {
+ $text = null;
+ }
+ Database::exec('UPDATE machine SET notes = :text WHERE machineuuid = :uuid', array(
+ 'uuid' => $uuid,
+ 'text' => $text,
+ ));
+ Message::addSuccess('notes-saved');
+ Util::redirect('?do=statistics&uuid=' . $uuid);
} elseif ($action === 'delmachines') {
$this->deleteMachines();
Util::redirect('?do=statistics', true);
@@ -204,14 +219,20 @@ class Page_Statistics extends Page
Message::addError('main.parameter-empty', 'uuid');
return;
}
+ $allowedLocations = User::getAllowedLocations("machine.delete");
+ if (empty($allowedLocations)) {
+ Message::addError('main.no-permission');
+ Util::redirect('?do=statistics');
+ }
$res = Database::simpleQuery('SELECT machineuuid, locationid FROM machine WHERE machineuuid IN (:ids)', compact('ids'));
$ids = array_flip($ids);
$delete = [];
- $allowedLocations = User::getAllowedLocations("delete");
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ unset($ids[$row['machineuuid']]);
if (in_array($row['locationid'], $allowedLocations)) {
- unset($ids[$row['machineuuid']]);
$delete[] = $row['machineuuid'];
+ } else {
+ Message::addError('locations.no-permission-location', $row['locationid']);
}
}
if (!empty($delete)) {
@@ -219,7 +240,6 @@ class Page_Statistics extends Page
Message::addSuccess('deleted-n-machines', count($delete));
}
if (!empty($ids)) {
- // TODO: Warn permissions
Message::addWarning('unknown-machine', implode(', ', array_keys($ids)));
}
}
@@ -237,8 +257,6 @@ class Page_Statistics extends Page
return;
}
- $show = Request::get('show', 'stat', 'string');
-
/* read filter */
$this->query = Request::any('filters', false);
if ($this->query === false) {
@@ -251,23 +269,31 @@ class Page_Statistics extends Page
$filterSet = new FilterSet($filters);
$filterSet->setSort($sortColumn, $sortDirection);
- if ($show == 'list') {
+ if (!$filterSet->setAllowedLocationsFromPermission('view.' . $this->show)) {
+ Message::addError('main.no-permission');
+ Util::redirect('?do=main');
+ }
+
+ if ($this->show === 'list') {
Render::openTag('div', array('class' => 'row'));
$this->showFilter('list', $filterSet);
Render::closeTag('div');
$this->showMachineList($filterSet);
return;
+ } elseif ($this->show === 'summary') {
+ $filterSet->filterNonClients();
+ Render::openTag('div', array('class' => 'row'));
+ $this->showFilter('summary', $filterSet);
+ $this->showSummary($filterSet);
+ $this->showMemory($filterSet);
+ $this->showId44($filterSet);
+ $this->showKvmState($filterSet);
+ $this->showLatestMachines($filterSet);
+ $this->showSystemModels($filterSet);
+ Render::closeTag('div');
+ } else {
+ Message::addError('main.value-invalid', 'show', $this->show);
}
- $filterSet->filterNonClients();
- Render::openTag('div', array('class' => 'row'));
- $this->showFilter('stat', $filterSet);
- $this->showSummary($filterSet);
- $this->showMemory($filterSet);
- $this->showId44($filterSet);
- $this->showKvmState($filterSet);
- $this->showLatestMachines($filterSet);
- $this->showSystemModels($filterSet);
- Render::closeTag('div');
}
/**
@@ -295,15 +321,17 @@ class Page_Statistics extends Page
$locsFlat = array();
if (Module::isAvailable('locations')) {
+ $allowed = $filterSet->getAllowedLocations();
foreach (Location::getLocations() as $loc) {
$locsFlat['L' . $loc['locationid']] = array(
'pad' => $loc['locationpad'],
'name' => $loc['locationname'],
- 'disabled' => !in_array($loc['locationid'], $this->locationsAllowedToView)
+ 'disabled' => $allowed !== false && !in_array($loc['locationid'], $allowed),
);
}
}
+ Permission::addGlobalTags($data['perms'], null, ['view.summary', 'view.list']);
$data['locations'] = json_encode($locsFlat);
Render::addTemplate('filterbox', $data);
@@ -355,8 +383,6 @@ class Page_Statistics extends Page
private function showSummary($filterSet)
{
$filterSet->makeFragments($where, $join, $sort, $args);
- $args['allowedLocations'] = $this->locationsAllowedToView;
- $where = "locationid IN (:allowedLocations) AND ($where)";
$known = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE $where", $args);
// If we only have one machine, redirect to machine details
if ($known['val'] == 1) {
@@ -417,8 +443,6 @@ class Page_Statistics extends Page
global $STATS_COLORS;
$filterSet->makeFragments($where, $join, $sort, $args);
- $args['allowedLocations'] = $this->locationsAllowedToView;
- $where = "locationid IN (:allowedLocations) AND ($where)";
$res = Database::simpleQuery('SELECT systemmodel, Round(AVG(realcores)) AS cores, Count(*) AS `count` FROM machine'
. " $join WHERE $where GROUP BY systemmodel ORDER BY `count` DESC, systemmodel ASC", $args);
$lines = array();
@@ -451,8 +475,6 @@ class Page_Statistics extends Page
global $STATS_COLORS, $SIZE_RAM;
$filterSet->makeFragments($where, $join, $sort, $args);
- $args['allowedLocations'] = $this->locationsAllowedToView;
- $where = "locationid IN (:allowedLocations) AND ($where)";
$res = Database::simpleQuery("SELECT mbram, Count(*) AS `count` FROM machine $join WHERE $where GROUP BY mbram", $args);
$lines = array();
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
@@ -498,8 +520,6 @@ class Page_Statistics extends Page
private function showKvmState($filterSet)
{
$filterSet->makeFragments($where, $join, $sort, $args);
- $args['allowedLocations'] = $this->locationsAllowedToView;
- $where = "locationid IN (:allowedLocations) AND ($where)";
$colors = array('UNKNOWN' => '#666', 'UNSUPPORTED' => '#ea5', 'DISABLED' => '#e55', 'ENABLED' => '#6d6');
$res = Database::simpleQuery("SELECT kvmstate, Count(*) AS `count` FROM machine $join WHERE $where GROUP BY kvmstate ORDER BY `count` DESC", $args);
$lines = array();
@@ -523,8 +543,6 @@ class Page_Statistics extends Page
global $STATS_COLORS, $SIZE_ID44;
$filterSet->makeFragments($where, $join, $sort, $args);
- $args['allowedLocations'] = $this->locationsAllowedToView;
- $where = "locationid IN (:allowedLocations) AND ($where)";
$res = Database::simpleQuery("SELECT id44mb, Count(*) AS `count` FROM machine $join WHERE $where GROUP BY id44mb", $args);
$lines = array();
$total = 0;
@@ -576,8 +594,6 @@ class Page_Statistics extends Page
private function showLatestMachines($filterSet)
{
$filterSet->makeFragments($where, $join, $sort, $args);
- $args['allowedLocations'] = $this->locationsAllowedToView;
- $where = "locationid IN (:allowedLocations) AND ($where)";
$args['cutoff'] = ceil(time() / 3600) * 3600 - 86400 * 10;
$res = Database::simpleQuery("SELECT machineuuid, clientip, hostname, firstseen, mbram, kvmstate, id44mb FROM machine $join"
@@ -611,8 +627,6 @@ class Page_Statistics extends Page
{
Module::isAvailable('js_stupidtable');
$filterSet->makeFragments($where, $join, $sort, $args);
- $args['allowedLocations'] = $this->locationsAllowedToView;
- $where = "locationid IN (:allowedLocations) AND ($where)";
$xtra = '';
if ($filterSet->isNoId44Filter()) {
$xtra .= ', data';
@@ -629,14 +643,17 @@ class Page_Statistics extends Page
. " $join WHERE $where $sort", $args);
$rows = array();
$singleMachine = 'none';
- $deleteAllowedLocations = User::getAllowedLocations("delete");
+ $deleteAllowedLocations = User::getAllowedLocations("machine.delete");
+ $detailsAllowedLocations = User::getAllowedLocations("machine.view-details");
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
if ($singleMachine === 'none') {
$singleMachine = $row['machineuuid'];
} else {
$singleMachine = false;
}
- $row['deleteAllowed'] = in_array($row['locationid'], $deleteAllowedLocations);
+ // TODO: This only makes sense as long as there is only one action to perform on selected clients; reboot/shutdown is planned
+ $row['delete_disabled'] = in_array($row['locationid'], $deleteAllowedLocations) ? '' : 'disabled';
+ $row['link_details'] = in_array($row['locationid'], $detailsAllowedLocations);
$row['state_' . $row['state']] = true;
//$row['firstseen'] = Util::prettyTime($row['firstseen']);
$row['lastseen_int'] = $row['lastseen'];
@@ -668,12 +685,13 @@ class Page_Statistics extends Page
$row['modeName'] = $data['modeName'];
}
}
+ $row['locationname'] = Location::getName($row['locationid']);
$rows[] = $row;
}
if ($singleMachine !== false && $singleMachine !== 'none') {
Util::redirect('?do=statistics&uuid=' . $singleMachine);
}
- Render::addTemplate('clientlist', array(
+ $data = array(
'rowCount' => count($rows),
'rows' => $rows,
'query' => $this->query,
@@ -684,7 +702,8 @@ class Page_Statistics extends Page
'showList' => 1,
'show' => 'list',
'redirect' => $_SERVER['QUERY_STRING']
- ));
+ );
+ Render::addTemplate('clientlist', $data);
}
private function ramColorClass($mb)
@@ -755,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'"
@@ -774,23 +793,23 @@ 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);
return;
}
- if (!in_array($client['locationid'], $this->locationsAllowedToView)) {
- Message::addError('main.no-permission');
- return;
- }
+ User::assertPermission('machine.view-details', (int)$client['locationid']);
// Hack: Get raw collected data
if (Request::get('raw', false)) {
Header('Content-Type: text/plain; charset=utf-8');
@@ -820,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 {
@@ -829,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']);
@@ -856,7 +881,7 @@ class Page_Statistics extends Page
Parser::parsePci($client['lspci1'], $client['lspci2'], $section[2]);
}
if (isset($hdds['hdds']) && $section[1] === 'smartctl') {
- // This currently required that the partition table section comes first...
+ // This currently requires that the partition table section comes first...
Parser::parseSmartctl($hdds['hdds'], $section[2]);
}
}
@@ -891,6 +916,7 @@ class Page_Statistics extends Page
$client['screens'][] = $row;
}
array_multisort($ports, SORT_ASC, $client['screens']);
+ Permission::addGlobalTags($client['perms'], null, ['hardware.projectors.edit', 'hardware.projectors.view']);
// Throw output at user
Render::addTemplate('machine-main', $client);
// Sessions
@@ -899,16 +925,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
@@ -934,9 +962,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';
@@ -956,8 +987,26 @@ class Page_Statistics extends Page
}
if ($client['state'] === 'OCCUPIED') {
$spans['graph'] .= '<div style="background:#e99;left:' . round(($client['logintime'] - $cutoff) * $scale, 2) . '%;width:' . round(($NOW - $client['logintime'] + 900) * $scale, 2) . '%">&nbsp;</div>';
+ $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'] .= '<div style="background:#444;left:' . round(($client['lastseen'] - $cutoff) * $scale, 2) . '%;width:' . round(($NOW - $client['lastseen'] + 900) * $scale, 2) . '%">&nbsp;</div>';
+ $spans['rows'][] = [
+ 'from' => Util::prettyTime($client['lastseen']),
+ 'duration' => '-',
+ 'glyph' => 'off',
+ ];
+ } elseif ($client['state'] === 'STANDBY') {
+ $spans['graph'] .= '<div style="background:#686;left:' . round(($client['lastseen'] - $cutoff) * $scale, 2) . '%;width:' . round(($NOW - $client['lastseen'] + 900) * $scale, 2) . '%">&nbsp;</div>';
+ $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) {
@@ -1002,8 +1051,10 @@ class Page_Statistics extends Page
));
}
// Notes
- $client["notesAllowed"] = User::hasPermission("note", $client["locationid"]);
- Render::addTemplate('machine-notes', $client);
+ if (User::hasPermission('machine.note.*', (int)$client['locationid'])) {
+ Permission::addGlobalTags($client['perms'], (int)$client['locationid'], ['machine.note.edit']);
+ Render::addTemplate('machine-notes', $client);
+ }
}
private function eventToIconName($event)
@@ -1026,6 +1077,8 @@ class Page_Statistics extends Page
protected function doAjax()
{
+ if (!User::load())
+ return;
$param = Request::any('lookup', false, 'string');
if ($param === false) {
die('No lookup given');
@@ -1089,3 +1142,5 @@ class Page_Statistics extends Page
), true);
}
}
+
+Page_Statistics::initConstants();
diff --git a/modules-available/statistics/pages/projectors.inc.php b/modules-available/statistics/pages/projectors.inc.php
index cde542c6..cc808cf0 100644
--- a/modules-available/statistics/pages/projectors.inc.php
+++ b/modules-available/statistics/pages/projectors.inc.php
@@ -13,12 +13,13 @@ class SubPage
private static function handleProjector($action)
{
+ User::assertPermission('hardware.projectors.edit');
$hwid = Request::post('hwid', false, 'int');
if ($hwid === false) {
Util::traceError('Param hwid missing');
}
if ($action === 'addprojector') {
- Database::exec('INSERT INTO statistic_hw_prop (hwid, prop, value)'
+ Database::exec('INSERT IGNORE INTO statistic_hw_prop (hwid, prop, value)'
. ' VALUES (:hwid, :prop, :value)', array(
'hwid' => $hwid,
'prop' => 'projector',
@@ -43,6 +44,7 @@ class SubPage
private static function showProjectors()
{
+ User::assertPermission('hardware.projectors.*');
$res = Database::simpleQuery('SELECT h.hwname, h.hwid FROM statistic_hw h'
. " INNER JOIN statistic_hw_prop p ON (h.hwid = p.hwid AND p.prop = :projector)"
. " WHERE h.hwtype = :screen ORDER BY h.hwname ASC", array(
diff --git a/modules-available/statistics/permissions/permissions.json b/modules-available/statistics/permissions/permissions.json
index 97a49036..663a8dc4 100644
--- a/modules-available/statistics/permissions/permissions.json
+++ b/modules-available/statistics/permissions/permissions.json
@@ -1,5 +1,26 @@
-[
- "view",
- "note",
- "delete"
-] \ No newline at end of file
+{
+ "machine.delete": {
+ "location-aware": true
+ },
+ "machine.note.view": {
+ "location-aware": true
+ },
+ "machine.note.edit": {
+ "location-aware": true
+ },
+ "hardware.projectors.view": {
+ "location-aware": false
+ },
+ "hardware.projectors.edit": {
+ "location-aware": false
+ },
+ "machine.view-details": {
+ "location-aware": true
+ },
+ "view.summary": {
+ "location-aware": true
+ },
+ "view.list": {
+ "location-aware": true
+ }
+} \ No newline at end of file
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/clientlist.html b/modules-available/statistics/templates/clientlist.html
index d06eb4f7..18a5d10a 100644
--- a/modules-available/statistics/templates/clientlist.html
+++ b/modules-available/statistics/templates/clientlist.html
@@ -34,6 +34,11 @@
<span class="glyphicon glyphicon-filter"></span>
</button>
</td>
+ <td>
+ <button type="button" class="btn btn-default btn-xs" onclick="popupFilter('location')">
+ <span class="glyphicon glyphicon-filter"></span>
+ </button>
+ </td>
</tr>
<tr>
<th data-sort="string">{{lang_machine}}</th>
@@ -43,18 +48,17 @@
<th data-sort="int" class="text-right">{{lang_gbRam}}</th>
<th data-sort="int" class="text-right">{{lang_tmpGb}}</th>
<th data-sort="int">{{lang_cpuModel}}</th>
+ <th data-sort="string">{{lang_location}}</th>
</tr>
</thead>
<tbody>
{{#rows}}
<tr>
<td data-sort-value="{{hostname}}" class="text-nowrap">
- {{#deleteAllowed}}
- <div class="checkbox checkbox-inline">
- <input type="checkbox" name="uuid[]" value="{{machineuuid}}" class="deleteCheckboxes">
- <label></label>
- </div>
- {{/deleteAllowed}}
+ <div class="checkbox checkbox-inline">
+ <input type="checkbox" name="uuid[]" value="{{machineuuid}}" class="deleteCheckboxes" {{delete_disabled}}>
+ <label></label>
+ </div>
{{#hasnotes}}
<span class="glyphicon glyphicon-exclamation-sign pull-right"></span>
{{/hasnotes}}
@@ -70,7 +74,13 @@
{{#state_STANDBY}}
<span class="glyphicon glyphicon-off green" title="{{lang_machineStandby}}"></span>
{{/state_STANDBY}}
- <a href="?do=Statistics&amp;uuid={{machineuuid}}"><b>{{hostname}}</b></a>
+ {{#link_details}}
+ <a href="?do=Statistics&amp;uuid={{machineuuid}}">
+ {{/link_details}}
+ <b>{{hostname}}</b>
+ {{#link_details}}
+ </a>
+ {{/link_details}}
<div class="small">{{machineuuid}}</div>
{{#rmmodule}}
<div class="small">{{lang_runMode}}:
@@ -93,6 +103,7 @@
</div>{{/nohdd}}
</td>
<td data-sort-value="{{realcores}}">{{lang_realCores}}: {{realcores}}<div class="small">{{cpumodel}}</div></td>
+ <td data-sort-value="{{locationname}}">{{locationname}}</td>
</tr>
{{/rows}}
</tbody>
diff --git a/modules-available/statistics/templates/cpumodels.html b/modules-available/statistics/templates/cpumodels.html
index d684c914..d89a5b2f 100644
--- a/modules-available/statistics/templates/cpumodels.html
+++ b/modules-available/statistics/templates/cpumodels.html
@@ -19,11 +19,11 @@
<tr id="{{id}}" class="{{collapse}}">
<td data-sort-value="{{systemmodel}}" class="text-left text-nowrap filter-col" data-filter-col="systemmodel">
<table style="width:100%; table-layout: fixed;"><tr><td style="overflow:hidden;text-overflow: ellipsis;">
- <a class="filter-val" data-filter-val="{{systemmodel}}" href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~systemmodel={{urlsystemmodel}}">{{systemmodel}}</a>
+ <a class="filter-val" data-filter-val="{{systemmodel}}" href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~systemmodel={{urlsystemmodel}}">{{systemmodel}}</a>
</td></tr></table>
</td>
<td data-sort-value="{{cores}}" class="text-right filter-col" data-filter-col="realcores">
- <a class="filter-val" data-filter-val="{{cores}}" href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~realcores={{cores}}">{{cores}}</a>
+ <a class="filter-val" data-filter-val="{{cores}}" href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~realcores={{cores}}">{{cores}}</a>
</td>
<td class="text-right">{{count}}</td>
</tr>
diff --git a/modules-available/statistics/templates/filterbox.html b/modules-available/statistics/templates/filterbox.html
index 58b66a75..07aa7320 100644
--- a/modules-available/statistics/templates/filterbox.html
+++ b/modules-available/statistics/templates/filterbox.html
@@ -13,7 +13,7 @@
<select id="operatorSelect" name="operator" class="form-control col-4-xs"> </select>
</div>
<div class="form-group">
- <input name="argument" id="argumentInput" class="form-control col-4-xs"> </input>
+ <input name="argument" id="argumentInput" class="form-control col-4-xs">
<select name="argument" id="argumentSelect" class="form-control col-4-xs"> </select>
</div>
@@ -41,11 +41,11 @@
<div class="btn-group pull-right">
- <button class="btn btn-default {{statButtonClass}}" type="submit" name="show" value="stat">
+ <button class="btn btn-default {{statButtonClass}}" type="submit" name="show" value="summary" {{perms.view.summary.disabled}}>
<span class="glyphicon glyphicon-stats"></span>
{{lang_showVisualization}}
</button>
- <button class="btn btn-default {{listButtonClass}}" type="submit" name="show" value="list">
+ <button class="btn btn-default {{listButtonClass}}" type="submit" name="show" value="list" {{perms.view.list.disabled}}>
<span class="glyphicon glyphicon-list"></span>
{{lang_showList}}
</button>
@@ -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}}};
@@ -169,16 +172,16 @@ document.addEventListener("DOMContentLoaded", function () {
}));
});
/* also set the type of the input */
- if (columns[col]['type'] == 'date') {
+ if (columns[col]['type'] === 'date') {
$('#argumentInput').datepicker({format : 'yyyy-mm-dd'});
$('#argumentSelect').hide();
- } else if(columns[col]['type'] == 'enum') {
+ } else if(columns[col]['type'] === 'enum') {
$('#argumentSelect').empty();
$('#argumentInput').hide();
$('#argumentSelect').show();
columns[col]['values'].forEach(function (v) {
var t = v;
- var disabled = true;
+ var disabled = (col === 'location');
if (col === 'location' && slxLocations['L' + v]) {
t = slxLocations['L' + v].pad + ' ' + slxLocations['L' + v].name;
disabled = slxLocations['L' + v].disabled;
diff --git a/modules-available/statistics/templates/id44.html b/modules-available/statistics/templates/id44.html
index d3b1ab1c..de1c71ad 100644
--- a/modules-available/statistics/templates/id44.html
+++ b/modules-available/statistics/templates/id44.html
@@ -17,7 +17,7 @@
{{#rows}}
<tr id="tmpid{{gb}}" class="{{class}} {{collapse}}">
<td data-sort-value="{{gb}}" class="text-left text-nowrap">
- <a class="filter-val" data-filter-val="{{gb}}" href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~hddgb={{gb}}">{{gb}}&thinsp;GiB</a>
+ <a class="filter-val" data-filter-val="{{gb}}" href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~hddgb={{gb}}">{{gb}}&thinsp;GiB</a>
</td>
<td class="text-right">{{count}}</td>
</tr>
diff --git a/modules-available/statistics/templates/kvmstate.html b/modules-available/statistics/templates/kvmstate.html
index 3704eda0..efa3bad3 100644
--- a/modules-available/statistics/templates/kvmstate.html
+++ b/modules-available/statistics/templates/kvmstate.html
@@ -17,7 +17,7 @@
{{#rows}}
<tr id="kvm{{kvmstate}}">
<td data-sort-value="{{kvmstate}}" class="text-left text-nowrap">
- <a class="filter-val" data-filter-val="{{kvmstate}}" href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~kvmstate={{kvmstate}}">{{kvmstate}}</a>
+ <a class="filter-val" data-filter-val="{{kvmstate}}" href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~kvmstate={{kvmstate}}">{{kvmstate}}</a>
</td>
<td class="text-right">{{count}}</td>
</tr>
diff --git a/modules-available/statistics/templates/machine-hdds.html b/modules-available/statistics/templates/machine-hdds.html
index fd6cf1be..b839dfca 100644
--- a/modules-available/statistics/templates/machine-hdds.html
+++ b/modules-available/statistics/templates/machine-hdds.html
@@ -20,7 +20,7 @@
<div>{{lang_powerOnTime}}: {{s_PowerOnHours}}&thinsp;{{lang_hours}} ({{PowerOnTime}})</div>
{{/s_PowerOnHours}}
<div class="row">
- <div class="col-sm-6">
+ <div class="col-sm-7">
<table class="table table-condensed table-striped table-responsive">
<tr>
<th>{{lang_partName}}</th>
@@ -30,14 +30,14 @@
{{#partitions}}
<tr id="{{id}}">
<td>{{name}}</td>
- <td class="text-right">{{size}}&thinsp;GiB</td>
+ <td class="text-right text-nowrap">{{size}}&thinsp;GiB</td>
<td>{{type}}</td>
</tr>
{{/partitions}}
</table>
<div class="slx-bold">{{lang_total}}: {{size}}&thinsp;GiB</div>
</div>
- <div class="col-sm-6">
+ <div class="col-sm-5">
<canvas id="{{devid}}-chart" style="width:100%;height:250px"></canvas>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
diff --git a/modules-available/statistics/templates/machine-main.html b/modules-available/statistics/templates/machine-main.html
index d8f2c521..44f03a99 100644
--- a/modules-available/statistics/templates/machine-main.html
+++ b/modules-available/statistics/templates/machine-main.html
@@ -117,9 +117,23 @@
<tr class="{{ramclass}}">
<td class="text-nowrap">{{lang_ram}}</td>
<td>
- {{gbram}}&thinsp;GiB
- {{#maxram}}({{lang_maximumAbbrev}} {{maxram}}){{/maxram}}
- {{ramtype}}
+ <div>
+ {{gbram}}&thinsp;GiB
+ {{#maxram}}({{lang_maximumAbbrev}} {{maxram}}){{/maxram}}
+ {{ramtype}}
+ </div>
+ {{#live_memsize}}
+ <div class="meter">
+ <div class="text">{{live_memfree_s}} {{lang_free}}</div>
+ <div class="bar" style="width:{{live_mempercent}}%"></div>
+ </div>
+ {{/live_memsize}}
+ {{#live_swapsize}}
+ <div class="meter">
+ <div class="text">{{live_swapfree_s}} {{lang_free}}</div>
+ <div class="bar" style="width:{{live_swappercent}}%"></div>
+ </div>
+ {{/live_swapsize}}
</td>
</tr>
{{#extram}}
@@ -135,7 +149,17 @@
{{/extram}}
<tr class="{{hddclass}}">
<td class="text-nowrap">{{lang_tempPart}}</td>
- <td>{{gbtmp}}&thinsp;GiB</td>
+ <td>
+ <div>
+ {{gbtmp}}&thinsp;GiB
+ </div>
+ {{#live_tmpsize}}
+ <div class="meter">
+ <div class="text">{{live_tmpfree_s}} {{lang_free}}</div>
+ <div class="bar" style="width:{{live_tmppercent}}%"></div>
+ </div>
+ {{/live_tmpsize}}
+ </td>
</tr>
<tr class="{{kvmclass}}">
<td class="text-nowrap">{{lang_64bitSupport}}</td>
@@ -157,13 +181,19 @@
{{#hwname}}
<div class="pull-right btn-group btn-group-xs">
{{#projector}}
- <a href="?do=statistics&amp;show=projectors" class="btn btn-default">{{lang_projector}}</a>
+ <a href="?do=statistics&amp;show=projectors" class="btn btn-default {{perms.hardware.projectors.view.disabled}}">
+ {{lang_projector}}
+ </a>
<button form="delprojector" type="submit" name="hwid" value="{{hwid}}"
- class="btn btn-danger"><span class="glyphicon glyphicon-remove"></span></button>
+ class="btn btn-danger" {{perms.hardware.projectors.edit.disabled}}>
+ <span class="glyphicon glyphicon-remove"></span>
+ </button>
{{/projector}}
{{^projector}}
<button form="addprojector" type="submit" name="hwid" value="{{hwid}}"
- class="btn btn-success"><span class="glyphicon glyphicon-plus"></span> {{lang_projector}}</button>
+ class="btn btn-success" {{perms.hardware.projectors.edit.disabled}}>
+ <span class="glyphicon glyphicon-plus"></span> {{lang_projector}}
+ </button>
{{/projector}}
</div>
{{/hwname}}
diff --git a/modules-available/statistics/templates/machine-notes.html b/modules-available/statistics/templates/machine-notes.html
index 66e44da4..c352580f 100644
--- a/modules-available/statistics/templates/machine-notes.html
+++ b/modules-available/statistics/templates/machine-notes.html
@@ -8,9 +8,12 @@
<input type="hidden" name="token" value="{{token}}">
<input type="hidden" name="action" value="setnotes">
<input type="hidden" name="uuid" value="{{machineuuid}}">
- <textarea name="content" class="form-control" cols="101" rows="10" {{^notesAllowed}}disabled{{/notesAllowed}}>{{notes}}</textarea>
+ <textarea name="content" class="form-control" cols="101" rows="10" {{perms.machine.note.edit.disabled}}>{{notes}}</textarea>
<br/>
- <button type="submit" class="btn btn-primary pull-right" {{^notesAllowed}}disabled{{/notesAllowed}}><span class="glyphicon glyphicon-floppy-disk"></span> {{lang_save}}</button>
+ <button type="submit" class="btn btn-primary pull-right" {{perms.machine.note.edit.disabled}}>
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
</form>
</div>
</div>
diff --git a/modules-available/statistics/templates/memory.html b/modules-available/statistics/templates/memory.html
index 6bc13980..cfb86062 100644
--- a/modules-available/statistics/templates/memory.html
+++ b/modules-available/statistics/templates/memory.html
@@ -17,7 +17,7 @@
{{#rows}}
<tr id="ramid{{gb}}" class="{{class}} {{collapse}}">
<td class="text-left text-nowrap" data-sort-value="{{gb}}">
- <a class="filter-val" data-filter-val="{{gb}}" href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~gbram={{gb}}">{{gb}}&thinsp;GiB</a>
+ <a class="filter-val" data-filter-val="{{gb}}" href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~gbram={{gb}}">{{gb}}&thinsp;GiB</a>
</td>
<td class="text-right">{{count}}</td>
</tr>
diff --git a/modules-available/statistics/templates/summary.html b/modules-available/statistics/templates/summary.html
index fe9559ed..3ede7bc5 100644
--- a/modules-available/statistics/templates/summary.html
+++ b/modules-available/statistics/templates/summary.html
@@ -8,8 +8,8 @@
{{/runmode}}
<div>
{{lang_knownMachines}}: <b>{{known}}</b>&emsp;
- <a href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~state=on">{{lang_onlineMachines}}</a>: <b>{{online}}</b>&emsp;
- <a href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~state=occupied">{{lang_inUseMachines}}</a>: <b>{{used}}</b> (<b>{{usedpercent}}%</b>)
+ <a href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~state=on">{{lang_onlineMachines}}</a>: <b>{{online}}</b>&emsp;
+ <a href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~state=occupied">{{lang_inUseMachines}}</a>: <b>{{used}}</b> (<b>{{usedpercent}}%</b>)
</div>
{{#badhdd}}
<div>